Python入门自学进阶-Web框架——15Django的Form验证2

Posted kaoa000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——15Django的Form验证2相关的知识,希望对你有一定的参考价值。

对Form进行更深入的研究。

使用Form,需要
1、创建一个验证用户请求的模板,就是一个类:
from django import forms
class MyForm(forms.Form)
    user = forms.CharField(...)       # 默认生成 input type=‘text’
    email =  forms.EmailField(...)   # 默认生成input type=‘email’
    pwd = forms.PasswordField(...) # 默认生成input type=‘password’

通过这个模板,前端就可以自动生成相应的html标签,CharField(...)默认是生成一个input的text标签等等。类就是一个模板,里面有几个字段,就验证几个字段

2、类中要有相应的字段:user = forms.CharField(...)

字段是继承了Field类的对象,主要是用来验证输入的某个字段的数据的合法性,如可以在参数中定义长度等,这个类主要是封装了一些正则表达式。

当obj = MyForm(req.POST)
    obj.is_valid()     # 这个方法就是循环执行类中每个字段的验证规则,如这里是3个字段,如果有一个字段的验证结果为False,结果就为False,全正确,才为True。

3、插件:
对于字段,在前端生成HTML标签时,如果没有其他配置,会生成默认的标签的,就如CharField(...)默认是生成一个input的text标签。那是否可以改变这个默认生成的标签呢?可以使用插件来改变,就是在参数中配置“widget=”选项。如下形式

user = forms.CharField(...,widget=Input框) 

对于CharField类,看其源代码如下

 在CharField中没有widget,在其父类Field中定义了widget=TextInput

对于其他的字段类,如下:

 基本上都是在本类中定义了默认的widget。

django提供的字段类列表如下:

所以,在定义不同的字段时,是可以通过widget修改不同的插件来生成不同的标签,fields.py中的这些类都是插件。

如user = forms.CharField(widget=forms.PasswordField),生成的就是密码输入框,而不是默认的文本输入框。

还可以给生成的标签添加属性:

user = forms.CharField(widget=forms.TextField(attrs=‘class’:‘c1’,‘placeholder’:‘用户名’))

测试:

模板类:

from django import forms

class MyForm(forms.Form):
    user = forms.CharField()
    user1 = forms.CharField(widget=forms.PasswordInput)
    user2 = forms.CharField(widget=forms.TextInput(attrs='class':'cl1','placeholder':'用户名'))
    user3 = forms.ChoiceField(choices=[(1,'足球'),(2,'篮球'),(3,'排球'),(4,'乒乓球')])

视图函数:

def detail(req):
    obj = myviews.MyForm()
    return render(req,'detail.html','obj':obj)

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>user: obj.user </p>
    <p>user1: obj.user1 </p>
    <p>user2: obj.user2 </p>
    <p>user3: obj.user3 </p>
</body>
</html>

结果:

 

 模板的字段还具有自动类型转换的功能,如:

user4 = forms.IntegerField(),前端传来的是字符串,到了这里,会自动转换为整型数。

 字段的参数:

Field
    required=True,               是否必须输入值,即不能为空,默认设置
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 'required': '不能为空', 'invalid': '格式错误'
    show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=None            Label内容后缀
 
 
CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
 
BaseTemporalField(Field)
    input_formats=None          时间格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages='invalid': '...'
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否允许空文件
 
ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值
 
ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
 
SlugField(CharField)           数字,字母,下划线,减号(连字符)
    ...
 
UUIDField(CharField)           uuid类型
    ...

测试:

后端:

from django.shortcuts import render,HttpResponse,redirect,HttpResponseRedirect
from myadminzdy import models
from django import forms
from django.core.validators import RegexValidator
class MyForm(forms.Form):
    # user = forms.CharField()
    # user1 = forms.CharField(widget=forms.PasswordInput)
    # user2 = forms.CharField(widget=forms.TextInput(attrs='class':'cl1','placeholder':'用户名'))
    # user3 = forms.ChoiceField(choices=[(1,'足球'),(2,'篮球'),(3,'排球'),(4,'乒乓球')],)
    f1 = forms.CharField()        #默认字段不能为空,即required=True
    f2 = forms.CharField(required=False)   # 字段可以为空
    f3 = forms.CharField(label="f3的label") # 前端可以使用.label、.id_for_label、.label_tag
    f4 = forms.CharField(initial='初始值')  # 生成的标签有初始值
    f5 = forms.CharField(initial='初始值11111',show_hidden_initial=True)  #在生成一个显示的标签外,还生成一个隐藏的标签,其值保持原始值,可用于标签值的比对
    f6 =forms.CharField(validators=[RegexValidator(r'^[0-9]+$','jiaoyan1:quanshuzi'),RegexValidator(r'^135[0-9]+$','jiaoyan2:135kaishi')])
    # 添加自定义的校验规则,使用validators=参数,规则是RegexValidator的对象,这里添加两条,它的两个参数,一个是规则,一个是错误信息
    # 打印的错误信息:"f6": ["message": "jiaoyan1:quanshuzi", "code": "invalid", "message": "jiaoyan2:135kaishi", "code": "invalid"]
    f7 = forms.CharField(validators=[RegexValidator(r'^[0-9]+$', 'jiaoyan1:quanshuzi'),
                                     RegexValidator(r'^135[0-9]+$', 'jiaoyan2:135kaishi')],
                         error_messages='required':'111-buweikong','invalid':'222-geshicuowu')
    # 测试error_message参数
    # "f6": ["message": "jiaoyan1:quanshuzi", "code": "invalid", "message": "jiaoyan2:135kaishi", "code": "invalid"],
    #  "f7": ["message": "222-geshicuowu", "code": "invalid", "message": "222-geshicuowu", "code": "invalid"]
    # 可以看到,设置error_message后,最后的错误信息以error_message设置的为主
    # 对于RegexValidator,还可以第三个参数:code,来定义不同的类型,如系统中的required、invalid等
    f8 = forms.CharField(validators=[RegexValidator(r'^[0-9]+$', 'jiaoyan1:quanshuzi',code='f1'),
                                     RegexValidator(r'^135[0-9]+$', 'jiaoyan2:135kaishi',code='f2')],
                         error_messages='required':'111-buweikong','invalid':'222-geshicuowu')
    # "f6": ["message": "jiaoyan1:quanshuzi", "code": "invalid", "message": "jiaoyan2:135kaishi", "code": "invalid"],
    #  "f7": ["message": "222-geshicuowu", "code": "invalid", "message": "222-geshicuowu", "code": "invalid"],
    #  "f8": ["message": "jiaoyan1:quanshuzi", "code": "f1", "message": "jiaoyan2:135kaishi", "code": "f2"]
    # error_message优先级高,根据code进行覆盖,想要修改原生的错误信息,可以设置error_message。
    f9 = forms.RegexField(r'^135[0-9]+$')  # 自定义正则表达式,自定义校验规则的字段,前端默认生成文本输入框
    f10 = forms.FileField()  # 上传文件,生成input type为file的标签,
    # clean()中显示的:'f10': <InMemoryUploadedFile: 1.jpg (image/jpeg)>
    f11 = forms.ImageField() # 类似FileField,生成标签多了accept="image/*"
    f12 = forms.ChoiceField(
        choices=[(1,'羽毛球'),(2,'乒乓球'),(3,'蓝球'),(4,'排球')],
        initial=3
    )
    # 下列选择框,值是字符串
    f13 = forms.TypedChoiceField(
        coerce=lambda x:int(x),  #类型转换
        choices=[(1, '羽毛球'), (2, '乒乓球'), (3, '蓝球'), (4, '排球')],
        initial=3
    )
    #f12和f13结果进行比较:'f12': '3', 'f13': 3,一个是字符串,一个是整型
    f14 = forms.MultipleChoiceField(
        choices=[(1, '羽毛球'), (2, '乒乓球'), (3, '蓝球'), (4, '排球')],
        initial=[1,3]
    )
    # 多选框,要注意初始值是列表。
    f15 = forms.FilePathField(path='myadminzdy/',allow_folders=True,allow_files=True,recursive=True)
    # 下拉选择框,内容是对应路径下的文件
def detail(req):
    if req.method == "GET":
        obj = MyForm()
        return render(req,'detail.html','obj':obj)
    else:
        obj = MyForm(req.POST,req.FILES)
        # 如果要接收文件,参数中增加req.FILES,因为上传的文件是保存在FILES中的
        obj.is_valid()
        print(obj.clean())
        print(obj.errors.as_json())
        return render(req,'detail.html','obj':obj)

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<script src="/static/jquery-3.6.0.js"></script>
<script>
    $('#id_f5').attr(value)
</script>
<body>
<form action="detail.html" method="post" enctype="multipart/form-data">
    % csrf_token %
    <p>f1: obj.f1 </p>
    <p>f2: obj.f2 </p>
    <!-- <p> obj.f3.label : obj.f3 </p> 这种写法标签只是一个文本 -->
    <!--<p> obj.f3.id_for_label  obj.f3 </p> --> <!-- 还有一个属性id_for_label,将id值做标签 -->
    <!-- <p><label for=" obj.f3.id_for_label "> obj.f3.label </label> obj.f3 </p>
     这种写法,点击标签,焦点进入相应的输入框 -->
     obj.f3.label_tag  obj.f3  <!-- 上面写法可以这样实现 -->
    <p>测试初始值 obj.f4 </p>
    <p>测试生成隐藏标签 obj.f5 </p>
    <!-- <input type="text" name="f5" value="初始值11111"  id="id_f5">
         <input type="hidden" name="initial-f5" value="初始值11111" id="initial-id_f5">
     -->
    <p>自定义校验规则 :  obj.f6 </p>
    <p>自定义校验规则 :  obj.f7 </p>
    <p>自定义校验规则 :  obj.f8 </p>
    <p>自定义校验规则 :  obj.f9 </p>
    <p>文件上传:  obj.f10 </p>
    <p>tuoian上传:  obj.f11 </p>
    <p>下拉选择框:  obj.f12 </p>
    <p>下拉选择框类型转换:  obj.f13 </p>
    <p>下拉多选选择框:  obj.f14 </p>
    <p>下拉选择框-文件列表:  obj.f15 </p>
    <p><input type="submit" value="提交"></p>
</form>
</body>
</html>

django提供的widget插件:

TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
Radioselect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
# 单radio,值为字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
 
# 单radio,值为字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.RadioSelect
# )
 
# 单select,值为字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
 
# 单select,值为字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.Select
# )
 
# 多选select,值为列表
# user = fields.MultipleChoiceField(
#     choices=((1,'上海'),(2,'北京'),),
#     initial=[1,],
#     widget=widgets.SelectMultiple
# )
 
 
# 单checkbox
# user = fields.CharField(
#     widget=widgets.CheckboxInput()
# )
 
 
# 多选checkbox,值为列表
# user = fields.MultipleChoiceField(
#     initial=[2, ],
#     choices=((1, '上海'), (2, '北京'),),
#     widget=widgets.CheckboxSelectMultiple
# )

对于字段,主要是进行验证的,对于widget插件,主要是定义前端生成的标签类型,如果字段定义的是CharField,而插件使用的是MultipleSelect,前端返回的是一个列表,但是验证是按字符串进行验证,就失去验证的意义,所以要做好两者的匹配。

下拉单选框,通过数据库动态获取数据:

页面:

 后端代码:

# models中代码,生成数据库
class UserType(models.Model):
    caption = models.CharField(max_length=32)


# 视图函数中
# 定义模板类
from myadminzdy import models
class MyFormDb(forms.Form):
    host = forms.CharField()
    host_type = forms.IntegerField(
        # widget=forms.Select(choices=[(1,'BJ'),(2,'SH')])    # 静态的获取
        # widget = forms.Select(choices=models.UserType.objects.all().values_list('id','caption'))
        # 从数据库动态获取,django启动时获取,只执行一次
        widget=forms.Select(choices=[])
    )
    # 为了在数据库记录改变时,前端动态获取到最新的数据,需要每次生成对象时执行一次获取数据
    def __init__(self,*args,**kwargs):
        super(MyFormDb,self).__init__(*args,**kwargs)
        self.fields['host_type'].widget.choices = models.UserType.objects.all().values_list('id','caption')
        # 每次实例化都会重新获取数据库数据并赋值给choices。

#视图函数
def db(req):
    if req.method == "GET":
        obj = MyFormDb()
        return render(req, 'db.html', 'obj': obj)
    else:
        obj = MyForm(req.POST, req.FILES)
        # 如果要接收文件,参数中增加req.FILES,因为上传的文件是保存在FILES中的
        obj.is_valid()
        print(obj.clean())
        print(obj.errors.as_json())
        return render(req, 'db.html', 'obj': obj)

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
     obj.host 
     obj.host_type 
</body>
</html>

关键点是,在定义模板类时,按照以前的写法,只定义字段,在定义字段时设置choices获取数据库数据,测试时,在后台数据库改变时,前端数据没有改变,因为字段的choices值只在第一次加载时执行了一次,以后都是这个加载值的拷贝,所以需要定义__init__(),在每次实例化时都执行一遍查询数据库,并赋值给choices,这样就实现数据的实时更新。

注意这一句:
self.fields['host_type'].widget.choices = models.UserType.objects.all().values_list('id','caption')

通过这个推断,模板类中有一个字典类型字段fields,我们定义的字段作为其中的键值对,所以可以使用fields['host_type']获取到forms.IntegerField(widget=forms.Select(choices=[])),这又是一个字典结构,可以通过.widget获取到forms.Select(choices=[]),又是一个字典结构,在.choices获取到choices。

定义一个用户表:

class UserType(models.Model):
    caption = models.CharField(max_length=32)

class User(models.Model):
    username = models.CharField(max_length=32)
    user_type = models.ForeignKey('UserType',on_delete=models.DO_NOTHING)

前台请求传递一个用户id,返回其用户名和类型:

def db(req):
    if req.method == "GET":
        nid = req.GET.get('nid')
        m = models.User.objects.filter(id=nid).first()
        dic = 'host':m.username,'host_type':m.user_type_id
        obj = MyFormDb(dic)
        return render(req, 'db.html', 'obj': obj)

只需要形成一个验证模板类字段的字典格式数据,这里就是dic ='host':'','host_type':'',传递给生成的模板对象就可以了。

以上是关于Python入门自学进阶-Web框架——15Django的Form验证2的主要内容,如果未能解决你的问题,请参考以下文章

Python入门自学进阶-Web框架——18FormModelForm

Python入门自学进阶-Web框架——20Django其他相关知识2

Python入门自学进阶-Web框架——2Django初识

Python入门自学进阶-Web框架——3Django的URL配置

Python入门自学进阶-Web框架——21DjangoAdmin项目应用

Python入门自学进阶-Web框架——21DjangoAdmin项目应用