第九篇.form组件

1.1Form介绍

我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。

#form组件的主要功能如下:
    1.生成页面可用的HTML标签
    2.对用户提交的数据进行校验
    3.保留上次输入内容

1.2普通注册功能的实现

1 from django.conf.urls import url
2 from django.contrib import admin
3 from app01 import views
4 ​
5 urlpatterns = [
6     url(r'^admin/', admin.site.urls),
7     url(r'^reg/', views.reg),
8 ]
urls.py
 1 # 注册
 2 def register(request):
 3     error_msg = ""
 4     if request.method == "POST":
 5         username = request.POST.get("name")
 6         pwd = request.POST.get("pwd")
 7         # 对注册信息做校验
 8         if len(username) < 6:
 9             # 用户长度小于6位
10             error_msg = "用户名长度不能小于6位"
11         else:
12             # 将用户名和密码存到数据库
13             return HttpResponse("注册成功")
14     return render(request, "register.html", {"error_msg": error_msg})
views.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>注册页面</title>
 6 </head>
 7 <body>
 8 <form action="/reg/" method="post">
 9     {% csrf_token %}
10     <p>
11         用户名:
12         <input type="text" name="name">
13     </p>
14     <p>
15         密码:
16         <input type="password" name="pwd">
17     </p>
18     <p>
19         <input type="submit" value="注册">
20         <p style="color: red">{{ error_msg }}</p>
21     </p>
22 </form>
23 </body>
24 </html>
reg.html

1.3使用form组件实现注册功能

1 from django.conf.urls import url
2 from django.contrib import admin
3 from app01 import views
4 ​
5 urlpatterns = [
6     url(r'^admin/', admin.site.urls),
7     url(r'^reg2/', views.reg),
8 ]
urls.py
 1 from django import forms
 2 ​
 3 # 1.先定义好一个form组件:
 4 class RegForm(forms.Form):
 5     username = forms.CharField(label='用户名',min_length=6)   #min_length 用于前端校验
 6     pwd = forms.CharField(label="密码",widget=forms.PasswordInput)   #widget用于密文
 7     gender = forms.ChoiceField(choices=((1,'男'),('2','女')))
 8     
 9 #2.使用form组件实现注册方式
10 def register2(request):
11     form_obj = RegForm()
12     if request.method == "POST":
13         # 实例化form对象的时候,把post提交过来的数据直接传进去
14         form_obj = RegForm(data=request.POST)
15         # 调用form_obj校验数据的方法
16         if form_obj.is_valid():     #对数据进行校验
17             return HttpResponse("注册成功")
18     return render(request, "register2.html", {"form_obj": form_obj})
views.py
 1 <!--  novalidate 前端不需校验-->
 2 ​
 3 <!DOCTYPE html>
 4 <html lang="en">
 5 <head>
 6     <meta charset="UTF-8">
 7     <title>注册2</title>
 8 </head>
 9 <body>
10     <form action="/reg2/" method="post" novalidate autocomplete="off">
11         {% csrf_token %}
12         <div>
13             
14             <!--
15                 {{ form_obj.as_p }}     生成一个个p标签
16                 {{ form_obj.errors }}   form表单中所有错误
17                 {{ form_obj.username }} 一个字段对应的input框
18                 {{ form_obj.username.label }}   该字段的中文提示
19                 {{ form_obj.username.id_for_label }}   该字段input框对应的id
20                 {{ form_obj.username.errors }}   该字段所有错误
21                 {{ form_obj.username.errors.0 }}   该字段的第一个错误
22             -->
23             
24             
25             
26             <label for="{{ form_obj.name.id_for_label }}">{{ form_obj.name.label }}</label>
27             {{ form_obj.name }} {{ form_obj.name.errors.0 }}
28         </div>
29         <div>
30             <label for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</label>
31             {{ form_obj.pwd }} {{ form_obj.pwd.errors.0 }}
32         </div>
33         <div>
34             <input type="submit" class="btn btn-success" value="注册">
35         </div>
36     </form>
37 </body>
38 </html>
reg2.html

1.4常用字段与插件

创建Form类时,主要涉及到 【字段】 和 【插件】,

  • 字段用于对用户请求数据的验证

  • 插件用于自动生成HTML

1.4.1 initial

初始值,input框里面的初始值。

from django import forms
class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三"  # 设置默认值
    )

1.4.2 error_messages

重写错误信息。

from django import forms

class RegForm(forms.Form):
    username = forms.CharField(
        label='用户名',
        min_length=6,
        initial='张三',
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        }
    )

1.4.3 password

class LoginForm(forms.Form):
    ...
    pwd = forms.CharField(
        min_length=6,
        label="密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
    )

1.4.4 radioSelect

单radio值为字符串

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        }
    )
    pwd = forms.CharField(min_length=6, label="密码")
    gender = forms.fields.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "保密")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )

1.4.5 单选Select

class LoginForm(forms.Form):
    ...
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )

1.4.6 多选Select

class LoginForm(forms.Form):
    ...
    hobby = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )

1.4.7 单选checkbox

class LoginForm(forms.Form):
    ...
    keep = forms.fields.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )

1.4.8 多选checkbox

class LoginForm(forms.Form):
    ...
    hobby = forms.fields.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )

关于choice的注意事项:

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 获取的值无法实时更新,那么需要自定义构造方法从而达到此目的。

  • 方式一:

     1 from django.forms import Form
     2 from django.forms import widgets
     3 from django.forms import fields
     4 from app01 import models
     5 
     6 class RegForm(forms.Form):
     7     ...
     8     hobby = forms.ChoiceField(
     9         choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
    10         label="爱好",
    11         initial=3,
    12         widget=forms.widgets.Select()
    13     )
    14  
    15     def __init__(self, *args, **kwargs):
    16         super(RegForm,self).__init__(*args, **kwargs)
    17         self.fields['hobby'].choices = models.Hobby.objects.values_list('id','name')
    View Code
  • 方式二:ModelMultipleChoiceField

    1 from django import forms
    2 from django.forms import fields
    3 from django.forms import models as form_model
    4 
    5 class RegForm(forms.Form):
    6     ...
    7     hobby = forms.ModelMultipleChoiceField(
    8         query_set = models.Hobby.objects.all()
    9     )
    View Code

1.4.9Django Form所有内置字段

  1 Field
  2     required=True,               是否允许为空
  3     widget=None,                 HTML插件
  4     label=None,                  用于生成Label标签或显示内容
  5     initial=None,                初始值
  6     help_text='',                帮助信息(在标签旁边显示)
  7     error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
  8     validators=[],               自定义验证规则
  9     localize=False,              是否支持本地化
 10     disabled=False,              是否可以编辑
 11     label_suffix=None            Label内容后缀
 12  
 13  
 14 CharField(Field)
 15     max_length=None,             最大长度
 16     min_length=None,             最小长度
 17     strip=True                   是否移除用户输入空白
 18  
 19 IntegerField(Field)
 20     max_value=None,              最大值
 21     min_value=None,              最小值
 22  
 23 FloatField(IntegerField)
 24     ...
 25  
 26 DecimalField(IntegerField)
 27     max_value=None,              最大值
 28     min_value=None,              最小值
 29     max_digits=None,             总长度
 30     decimal_places=None,         小数位长度
 31  
 32 BaseTemporalField(Field)
 33     input_formats=None          时间格式化   
 34  
 35 DateField(BaseTemporalField)    格式:2015-09-01
 36 TimeField(BaseTemporalField)    格式:11:12
 37 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 38  
 39 DurationField(Field)            时间间隔:%d %H:%M:%S.%f
 40     ...
 41  
 42 RegexField(CharField)
 43     regex,                      自定制正则表达式
 44     max_length=None,            最大长度
 45     min_length=None,            最小长度
 46     error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 47  
 48 EmailField(CharField)      
 49     ...
 50  
 51 FileField(Field)
 52     allow_empty_file=False     是否允许空文件
 53  
 54 ImageField(FileField)      
 55     ...
 56     注:需要PIL模块,pip3 install Pillow
 57     以上两个字典使用时,需要注意两点:
 58         - form表单中 enctype="multipart/form-data"
 59         - view函数中 obj = MyForm(request.POST, request.FILES)
 60  
 61 URLField(Field)
 62     ...
 63  
 64  
 65 BooleanField(Field)  
 66     ...
 67  
 68 NullBooleanField(BooleanField)
 69     ...
 70  
 71 ChoiceField(Field)
 72     ...
 73     choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
 74     required=True,             是否必填
 75     widget=None,               插件,默认select插件
 76     label=None,                Label内容
 77     initial=None,              初始值
 78     help_text='',              帮助提示
 79  
 80  
 81 ModelChoiceField(ChoiceField)
 82     ...                        django.forms.models.ModelChoiceField
 83     queryset,                  # 查询数据库中的数据
 84     empty_label="---------",   # 默认空显示内容
 85     to_field_name=None,        # HTML中value的值对应的字段
 86     limit_choices_to=None      # ModelForm中对queryset二次筛选
 87      
 88 ModelMultipleChoiceField(ModelChoiceField)
 89     ...                        django.forms.models.ModelMultipleChoiceField
 90  
 91  
 92      
 93 TypedChoiceField(ChoiceField)
 94     coerce = lambda val: val   对选中的值进行一次转换
 95     empty_value= ''            空值的默认值
 96  
 97 MultipleChoiceField(ChoiceField)
 98     ...
 99  
100 TypedMultipleChoiceField(MultipleChoiceField)
101     coerce = lambda val: val   对选中的每一个值进行一次转换
102     empty_value= ''            空值的默认值
103  
104 ComboField(Field)
105     fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
106                                fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
107  
108 MultiValueField(Field)
109     PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
110  
111 SplitDateTimeField(MultiValueField)
112     input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
113     input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
114  
115 FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
116     path,                      文件夹路径
117     match=None,                正则匹配
118     recursive=False,           递归下面的文件夹
119     allow_files=True,          允许文件
120     allow_folders=False,       允许文件夹
121     required=True,
122     widget=None,
123     label=None,
124     initial=None,
125     help_text=''
126  
127 GenericIPAddressField
128     protocol='both',           both,ipv4,ipv6支持的IP格式
129     unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
130  
131 SlugField(CharField)           数字,字母,下划线,减号(连字符)
132     ...
133  
134 UUIDField(CharField)           uuid类型
View Code

1.5校验

1.5.1内置校验

required = True
min_length
max_length

1.5.2自定义校验

  • 方式一: 写函数

     1 from django.shortcuts import render,HttpResponse
     2 from django import forms
     3 from django.core.exceptions import ValidationError
     4 
     5 #自定义校验器
     6 def checkname(values):
     7     #通过校验规则,什么都不用干
     8     #未通过校验规则,
     9     if 'alex' in values:
    10         raise ValidationError('不允许使用')
    11 
    12 
    13 class RegForm(forms.Form):
    14     username = forms.CharField(
    15         label='用户名',
    16         validators= [checkname],
    17     )
    View Code
  • 方式二:内置校验器

     1 from django.shortcuts import render,HttpResponse
     2 from django import forms
     3 from django.core.validators import RegexValidator
     4 
     5 class RegForm(forms.Form):
     6     phone = forms.CharField(
     7         validators=[RegexValidator(r'^1[3-9]\d{9}$','手机号格式不正确')],
     8     )
     9 
    10 def reg2(request):
    11     form_obj = RegForm()
    12     if request.method == 'POST':
    13         form_obj = RegForm(request.POST)
    14         if form_obj.is_valid():
    15             return HttpResponse("注册成功")
    16     return render(request, "reg2.html", {"form_obj": form_obj})
    View Code
  • 方式三:Hook方法

    如下

1.5.3钩子

除了上面两种方式,我们还可以在Form类中定义钩子函数,来实现自定义的验证功能。

  • is_valid() 流程

     1 1. 执行 full_clean() 方法
     2     定义错误字典
     3     定义存放清洗数据的字典
     4 2. 执行 _clean_fields()方法
     5     循环所有字段,获取当前字段的值,并进行校验(内置校验规则,自定义校验规则)
     6     1> 通过校验
     7         self.clean_data[name] = value
     8         如果有局部钩子,执行校验
     9            通过校验     self.clean_data[name] = value
    10         未通过校验     self._errors中添加当前字段错误,并将self.clean_data中当前字段的值删除掉
    11     2> 未通过校验
    12         self._errors  中添加当前字段错误  
    13 3.执行全局钩子 clean() 方法
    14     1> 局部钩子
    15         def clean_username(self):
    16             # 通过校验规则,必须返回当前字段值
    17             # 未通过校验规则,抛出异常
    18             raise ValidationError('不允许使用')
    19     2> 全局钩子
    20         def clean(self):
    21             # 通过校验规则,必须返回当前字段值
    22             # 未通过校验规则,抛出异常 '__all__'
    23             pass
    View Code

局部钩子

from django.shortcuts import render,HttpResponse
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator

class RegForm(forms.Form):
    username = forms.CharField(
        label='用户名',
        min_length=6,
        initial='张三',
    )

    # 局部钩子
    def clean_username(self):
        # 通过校验规则,必须返回当前字段值
        # 未通过校验规则,
        v = self.cleaned_data.get('username')
        if 'alex1111' in v:
            raise ValidationError('不允许使用233')
        else:
            return v
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 
 9 <form action="" method="post" novalidate>
10     {% csrf_token %}
11 
12     <p>
13         <laber for="{{ form_obj.username.id_for_label }}">{{ form_obj.username.label }}</laber>
14         {{ form_obj.username }}
15         {{ form_obj.username.errors.0 }}
16     </p>
17 
18 </body>
19 </html>
View Code

全局钩子

 1 from django.shortcuts import render,HttpResponse
 2 from django import forms
 3 from django.core.exceptions import ValidationError
 4 
 5 class RegForm(forms.Form):
 6     pwd = forms.CharField(label='密码',widget=forms.PasswordInput)
 7     re_pwd = forms.CharField(label='确认密码',widget=forms.PasswordInput)
 8 
 9     def clean(self):
10         pwd = self.cleaned_data.get('pwd')
11         re_pwd = self.cleaned_data.get('re_pwd')
12         if pwd == re_pwd:
13             return self.cleaned_data
14         else:
15             self.add_error('re_pwd','两次密码不一致')
16             raise ValidationError('两次密码不一致')
View Code
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 
 9 <form action="" method="post" novalidate>
10     {% csrf_token %}
11 
12     <p>
13         <laber for="{{ form_obj.pwd.id_for_label }}">{{ form_obj.pwd.label }}</laber>
14         {{ form_obj.pwd }}
15         {{ form_obj.pwd.errors }}
16     </p>
17     <p>
18         <laber for="{{ form_obj.re_pwd.id_for_label }}">{{ form_obj.re_pwd.label }}</laber>
19         {{ form_obj.re_pwd }}
20         {{ form_obj.re_pwd.errors }}   
21     </p>
22 
23 
24     <button>注册</button>
25 </form>
26 
27 
28 </body>
29 </html>
View Code

参考博文 https://www.cnblogs.com/maple-shaw/artcles/9537309.html

 

posted @ 2020-10-31 05:15  Primrose  阅读(92)  评论(0)    收藏  举报