Django之Form详解

Django的Form主要具有一下几大功能:

  • 生成HTML标签
  • 验证用户数据(显示错误信息)
  • HTML Form提交保留上次提交数据
  • 初始化页面显示内容

 

1、创建Form类、View函数处理

 1 from django import forms
 2 from django.forms import widgets    # widgets插件相关功能,包括了HTMl中INPUT、select、checkbox、radio等等全部标签
 3 from django.forms import fields     # fields:字段相关功能
 4 
 5 
 6 class FM(forms.Form):
 7     """
 8         下面的所有字段名字必须和前端的变量name的值相同,否则拿不到值
 9         字段本身只用作验证的功能,fields负责生成HTML
10     """
11     user = fields.CharField(
12         error_messages={'required': '用户名不能为空.'},
13         widget=widgets.Textarea(attrs={'class': 'c1'}),     # widget设置表单字段在html页面的类型,attrs给表单标签设置属性
14         label='用户名',
15         initial='root',
16         help_text='请输入用户名(手机号、邮箱)'
17     )
18 
19     pwd = fields.CharField(
20         max_length=12,
21         min_length=6,
22         error_messages={'required': '密码不能为空', 'min_length': '密码长度不能小于6', 'max_length': '密码长度不能大于12'},
23         widget=widgets.PasswordInput(attrs={'class': 'c2'}),
24         label='密码',
25     )
26     email = fields.EmailField(error_messages={'required': '邮箱不能为空.', 'invalid': '邮箱格式错误'})  # 自定义错误信息
27 
28     f = fields.FileField()
29 
30     city = fields.ChoiceField(
31         choices=[(0, '上海'), (1, '北京'), (2, '东莞')]  # 每个元组中的第0个元素是前端select标签中的option标签的value属性
32     )
33     city1 = fields.MultipleChoiceField(
34         choices=[(0, '上海'), (1, '北京'), (2, '东莞')]
35     )
36 
37 
38 def fm(request):
39     if request.method == "GET":
40         '''
41         打开一个新的编辑页面把默认值都获取到:创建个字典,把类中的字段一一写为字典的key,字典的值即为默认值,可以通过models从数据库获取
42         传递对象到前端时加上initial=dic参数即可设置默认值
43         注意:字段名必须和自定义类中的字段一一对应
44         '''
45         dic = {
46             'user': 'r1',
47             'pwd': '123123',
48             'email': 'asd@asd',
49             'city1': 1,
50             'city2': [1, 2],
51         }
52         obj = FM(initial=dic)
53         return render(request, 'fm.html', {'obj': obj})  # 打开页面,并将表单验证类对象传到html生成表单标签,利用表单类自动创建表单标签
54     elif request.method == "POST":
55         obj = FM(request.POST)                   # 创建验证表单类,将用户请求POST对象传进Form类中进行表单验证
56         r1 = obj.is_valid()                      # is_valid方法:对form中每一个字段逐一进行验证,返回验证是否通过的布尔值
57         if r1:
58             print(obj.cleaned_data)                         # 以字典的形式返回正确信息
59             models.UserInf.objects.create(**obj.cleaned_data) # 利用cleaned_data实现注册
60         else:
61             # print(obj.errors.as_json())
62             # print(obj.errors['user'])       # errors方法包含了所有的错误信息,取值通过字典方式
63             print(obj.errors)
64         return render(request, 'fm.html', {'obj': obj})

 

2、生成HTML

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <form action="/app03/fm/" method="POST">
 9         {% csrf_token %}
10         <p>{{ obj.user.label }} {{ obj.user }} {{ obj.errors.user.0 }} {{ obj.user.help_text }}</p>
11         <p>{{ obj.pwd.label }} {{ obj.pwd }} {{ obj.errors.pwd.0 }}</p>
12         <p>{{ obj.email }} {{ obj.errors.email.0 }}</p>
13         <p><input type="submit" value="提交"></p>
14 
15 
16 {#        <p>{{ obj.f }} {{ obj.errors.f.0 }}</p>#}
17 
18         <p>{{ obj.city }}</p>
19         <p>{{ obj.city1 }}</p>
20     </form>
21 </body>
22 </html>
23         
 1 widget插件设置表单字段在html页面的类型,包括了HTMl中INPUT、select、checkbox、radio等等全部标签
 2 
 3   使用方式:widget=widgets.Textarea()  或widget=forms.Textarea(),推荐第一种写法
 4   Textarea(): <textarea>标签类型
 5   TextInput():<input>标签类型        
 6     
 7         
 8         attrs给表单标签设置属性,可以给标签元素设置class样式,和input标签的各种type属性
 9         pwd = forms.CharField(
10             widget=forms.TextInput(attrs={'type':'password','class':'c1'}),
11         )
12     
13     其他方法:
14     EmailInput、URLInput、PasswordInput等等,如需定制样式直接在后面加对应的属性attrs参数即可
15     widget=widgets.PasswordInput(attrs={'class': 'c2'})            
16         
扩展
 1 需要实例化手动写的form模块的FORM类 ,要将所有request的post数据传入比如  f1 = form.Form(request.POST) 这样前端的html的所有提交的input就会验证
 2 
 3  返回两种数据
 4  1 cleaned_data  提交的正确数据(字典)
 5  2 errors           错误信息
 6 
 7 cleaned_data获取验证通过后的所有提交数据,返回字典
 8 使用方式:obj.cleaned_data
 9 
10 errors获取验证错误信息,返回所有表单的错误信息对象
11 使用方法:obj.errors
12 
13 格式:
14 cleaned_data:以字典的形式返回(form验证通过)正确信息
15         {'user': 'asdasd', 'email': 'wuyuan@123.com', 'pwd': '123456'}
16         models.UserInf.objects.create(**obj.cleaned_data) # 利用cleaned_data实现注册
17         
18 errors  errors.as_json():
19         {"email": [{"code": "required", "message": "This field is required."}], 
20          "user": [{"code": "required", "message": "This field is required."}],
21          "pwd": [{"code": "required", "message": "This field is required."}]}
22 
23     # 自定义错误信息时,根据code的值来定义,如:
24     error_messages={'required': '邮箱不能为空.', 'invalid': '邮箱格式错误'}
25     error_messages={'required': '密码不能为空', 'min_length': '密码长度不能小于6', 'max_length': '密码长度不能大于12'}
26     
27     
28 错误信息返回到前端:
29         <p>{{ obj.user }} {{ obj.errors.user.0 }}</p>                # obj.user自动生成input标签,obj.errors.user.0 ,错误信息message的值
30         <p>{{ obj.user }} {{ obj.errors.user.0 }}</p>
31         <p>{{ obj.pwd }} {{ obj.errors.pwd.0 }}</p>
32         <p>{{ obj.email }} {{ obj.errors.email.0 }}</p>
33 
34         
35 利用表单类自动创建表单标签
36 
37 1、首先在get访问页面时,创建表单类不传参数,然后把这个表单类传到html页面,在html页面通过,表单类.字段名称,来自动创建表单标签
38 
39   每一种表单字段类型,都有一个默认的表单标签,这个标签是可以更改的,所以表单类可以创建任何表单标签
40         <p>{{ obj.user }} {{ obj.errors.user.0 }}</p>
41         <p>{{ obj.pwd }} {{ obj.errors.pwd.0 }}</p>
42         <p>{{ obj.email }} {{ obj.errors.email.0 }}</p>
43 2、在post访问时,再次创建表单类需要传参,将post请求对象传入表单类进行验证表单,如果表单不合法,将错误信息对象传到html显示错误信息,将这次创建的表单类传到html页面替换get时创建的表单类        
44 
45 
46 小结:
47 1、通过form可以做用户请求的验证
48 2、帮前端在页面生成HTML标签,且同时会保留上一次提交的内容在输入框中 
49     注意:生成HTML的功能在新URL方式(form提交)时使用,
50           而对于ajax请求方式(不会刷新,不会跳转URL)不用生成HTML的功能
小结

 

 

 

Form类

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

 

1、Django内置字段:

  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     show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
  9     validators=[],               自定义验证规则、正则表达式
 10         '''
 11             from django.forms import Form
 12             from django.forms import widgets
 13             from django.forms import fields
 14             from django.core.validators import RegexValidator
 15              
 16             class MyForm(Form):
 17                 user = fields.CharField(
 18                     validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
 19                 )
 20         '''
 21     localize=False,              是否支持本地化,jango默认使用UTC时间,加上这个参数则在页面时间加上8小时变为本地时间
 22     disabled=False,              是否可以编辑
 23     label_suffix=None            Label内容后缀
 24  
 25  
 26 CharField(Field)                 # 获取用户输入的字符串,可以进行长度限制、自定义正则表达式                            
 27     max_length=None,             最大长度
 28     min_length=None,             最小长度
 29     strip=True                   是否移除用户输入空白
 30 
 31     
 32  
 33 IntegerField(Field)
 34     max_value=None,              最大值
 35     min_value=None,              最小值
 36  
 37 FloatField(IntegerField)
 38     ...
 39  
 40 DecimalField(IntegerField)
 41     max_value=None,              最大值
 42     min_value=None,              最小值
 43     max_digits=None,             总长度
 44     decimal_places=None,         小数位长度
 45  
 46 BaseTemporalField(Field)
 47     input_formats=None          时间格式化   
 48  
 49 DateField(BaseTemporalField)    格式:2015-09-01
 50 TimeField(BaseTemporalField)    格式:11:12
 51 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 52  
 53 DurationField(Field)            时间间隔:%d %H:%M:%S.%f
 54     ...
 55  
 56 RegexField(CharField)            # 功能相当于CharField 结合 validators
 57     regex,                      自定制正则表达式
 58     max_length=None,            最大长度
 59     min_length=None,            最小长度
 60     error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 61  
 62 EmailField(CharField)      
 63     ...
 64  
 65 FileField(Field)
 66     allow_empty_file=False     是否允许空文件
 67  
 68 '''
 69         f = fields.FileField()
 70         <p>{{ obj.f }} {{ obj.errors.f.0 }}</p>
 71         在前端上传文件后,以字典形式保存在cleaned_data中{'f': '文件对象'}
 72 '''
 73 ImageField(FileField)      
 74     ...
 75     注:需要PIL模块,pip3 install Pillow
 76     以上两个字典使用时,需要注意两点:
 77         - form表单中 enctype="multipart/form-data"
 78         - view函数中 obj = MyForm(request.POST, request.FILES)
 79  
 80 URLField(Field)
 81     ...
 82  
 83  
 84 BooleanField(Field)  
 85     ...
 86  
 87 NullBooleanField(BooleanField)
 88     ...
 89  
 90 ChoiceField(Field)                # 生成下拉框
 91     ...
 92     choices=(),                选项,如:choices = [(0,'上海'),(1,'北京'),]
 93     required=True,             是否必填
 94     widget=None,               插件,默认select插件
 95     label=None,                Label内容
 96     initial=None,              初始值
 97     help_text='',              帮助提示
 98     
 99 MultipleChoiceField(ChoiceField)    # 生成可以多选的下拉框
100     ...
101 
151  
152 ModelChoiceField(ChoiceField)
153     ...                        django.forms.models.ModelChoiceField
154     queryset,                  # 查询数据库中的数据
155     empty_label="---------",   # 默认空显示内容
156     to_field_name=None,        # HTML中value的值对应的字段
157     limit_choices_to=None      # ModelForm中对queryset二次筛选
158      
159 ModelMultipleChoiceField(ModelChoiceField)
160     ...                        django.forms.models.ModelMultipleChoiceField
161  
162  
163      
164 TypedChoiceField(ChoiceField)
165     coerce = lambda val: val   对选中的值进行一次转换
166     empty_value= ''            空值的默认值
167  
168 
169  
170 TypedMultipleChoiceField(MultipleChoiceField)
171     coerce = lambda val: val   对选中的每一个值进行一次转换
172     empty_value= ''            空值的默认值
173  
174  
175  
176 ComboField(Field)
177     fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
178                                fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
179  
180 MultiValueField(Field)
181     PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
182  
183 SplitDateTimeField(MultiValueField)
184     input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
185     input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
186  
187 FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中,把一个路径下的所有文件列出来
188     path,                      文件夹路径
189     match=None,                正则匹配
190     recursive=False,           递归下面的文件夹
191     allow_files=True,          允许文件
192     allow_folders=False,       允许文件夹
193     required=True,
194     widget=None,
195     label=None,
196     initial=None,
197     help_text=''
198  
199 GenericIPAddressField            
200     protocol='both',           both,ipv4,ipv6支持的IP格式
201     unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
202  
203 SlugField(CharField)           数字,字母,下划线,减号(连字符)
204     ...
205  
206 UUIDField(CharField)           uuid类型

 

常用选择插件:

 1 # 单radio:
 2 
 3 # 单radio,值为字符串
 4 user = fields.CharField(
 5     initial=2,
 6     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
 7 )
 8  
 9 # 单radio,值为字符串
10 user = fields.ChoiceField(
11     choices=((1, '上海'), (2, '北京'),),
12     initial=2,
13     widget=widgets.RadioSelect
14 )
15 
16 
17 
18 
19 # 单select实现方式:
20 
21 # 方式一:利用widgets.Select方法 单select,值为字符串
22  user = fields.CharField(
23      initial=2,
24      widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
25  )
26  
27 # 方式二:利用fields.ChoiceField方法,单select,值为字符串
28 user = fields.ChoiceField(
29     choices=((1, '上海'), (2, '北京'),),
30     initial=2,
31     # widget=widgets.Select
32 )
33 
34 
35 使用示例:
36 class UserInfoForm(forms.Form): 
37     user_type = fields.ChoiceField(
38         # choices=[(1, '普通用户'), (2, '超级用户')],
39         choices=models.UserType.objects.values_list('id', 'name'),
40     )
41 
42     user_type2 = fields.CharField(
43         # widget=widgets.Select(choices=[(0, '上海'), (1, '北京'), (2, '东莞')])
44         widget=widgets.Select(choices=models.UserType.objects.values_list('id', 'name'))
45     )
46 
47  
48  
49  
50 # 多选select,利用fields.MultipleChoiceField方法,值为列表
51 user = fields.MultipleChoiceField(
52     choices=((1,'上海'),(2,'北京'),),
53     initial=[1,],
54     widget=widgets.SelectMultiple
55 )
56 
57 # 多选select,利用fields.CharField方法,值为列表
58 user = fields.CharField(
59     choices=((1,'上海'),(2,'北京'),),
60     initial=[1,],
61     widget=widgets.SelectMultiple(choices=((1,'上海'),(2,'北京'),))
62 )
63 
64 
65 使用示例:
66 class UserInfoForm(forms.Form):  
67     user_type3 = fields.MultipleChoiceField(
68         # choices=[(1, '普通用户'), (2, '超级用户')],
69         choices=models.UserType.objects.values_list('id', 'name'),
70     )
71  
72     user_type4 = fields.CharField(
73         # widget=widgets.SelectMultiple(choices=[(0, '上海'), (1, '北京'), (2, '东莞')])
74         widget=widgets.SelectMultiple(choices=models.UserType.objects.values_list('id', 'name'))
75     )
76     
77  
78  
79  
80  
81 # 单checkbox
82  user = fields.CharField(
83      widget=widgets.CheckboxInput()
84  )
85  
86  
87 # 多选checkbox,值为列表
88  user = fields.MultipleChoiceField(
89      initial=[2, ],
90      choices=((1, '上海'), (2, '北京'),),
91      widget=widgets.CheckboxSelectMultiple
92  )

 

 

 

 

2、Django内置插件:

 1 Django,Form内置插件:
 2 # 每个插件里面都可以加上attrs={} 来自定义属性
 3 
 4 TextInput(Input)
 5 NumberInput(TextInput)
 6 EmailInput(TextInput)
 7 URLInput(TextInput)
 8 PasswordInput(TextInput)
 9 HiddenInput(TextInput)
10 Textarea(Widget)
11 DateInput(DateTimeBaseInput)
12 DateTimeInput(DateTimeBaseInput)
13 TimeInput(DateTimeBaseInput)
14 CheckboxInput
15 Select
16 NullBooleanSelect
17 SelectMultiple
18 RadioSelect
19 CheckboxSelectMultiple
20 FileInput
21 ClearableFileInput
22 MultipleHiddenInput
23 SplitDateTimeWidget
24 SplitHiddenDateTimeWidget
25 SelectDateWidget
26         

 

 

 

动态select的两种实现方式:

方式一(推荐,不用改models):

通过重写父类构造方法,构造方法只有在创建类的实例的时候才会执行,利用构造方法的特性,实例化为对象时去数据库select可实现动态select获取,之后封装到对象中返回前端即可

 1 from django import forms
 2 from django.forms import fields
 3 from django.forms import widgets
 4 from app03 import models
 5 
 6 
 7 class UserInfoForm(forms.Form):
 8     user = fields.CharField(
 9         required=False,
10         widget=widgets.Textarea(attrs={'class': 'c1'})
11     )
12 
13     pwd = fields.CharField(
14         max_length=12,
15         widget=widgets.PasswordInput(attrs={'class': 'c1'})
16     )
17 # 针对两种select框创建方式分别实现动态select
18     user_type = fields.ChoiceField(
19         # choices=models.UserType.objects.values_list('id', 'name'),  # 构造方法中定义choices后,这里取的值没用了
20         choices=[]
21     )
22 
23     user_type2 = fields.CharField(
24         widget=widgets.Select(choices=[])
25     )
26 
27     def __init__(self, *args, **kwargs):
28         """
29             重写父类构造方法,构造方法只有在创建类的实例的时候才会执行,
30             利用构造方法的特性,实例化为对象时去数据库select可实现动态select获取,之后封装到对象中
31         """
32         super(UserInfoForm, self).__init__(*args, **kwargs)
33         self.fields['user_type'].choices = models.UserType.objects.values_list('id', 'name')     # 每次实例化为对象时都会去数据库中取数据
34         self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id', 'name') # 针对两种select框创建方式分别实现动态select
form.py
1 from app03.forms import UserInfoForm
2 
3 
4 def form(request):
5     if request.method == "GET":
6         obj = UserInfoForm()
7         return render(request, 'index.html', {'obj': obj})
views.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 
 7 </head>
 8 <body>
 9     {% csrf_token %}
10     <p>{{ obj.user_type }}</p>
11     <p>{{ obj.user_type2 }}</p>
12 </body>
13 </html>
index.html

 

方式二:

通过jango的ModelChoiceField类实现:

 

 1 forms.py: 
 2 from django.forms.models import ModelChoiceField,ModelMultipleChoiceField # ModelMultipleChoiceField 多选,用法同单选
 3 
 4 class UserInfoForm(forms.Form):
 5 
 6     user_type3 = ModelChoiceField(
 7         queryset=models.UserType.objects.all(),
 8         empty_label='请选择用户类型',
 9         to_field_name='id'      # HTML中select标签下option标签的value的值对应的字段
10     )
11 
12 
13 
14 
15 models.py:
16 
17 class UserType(models.Model):
18     name = models.CharField(max_length=32)
19 
20     def __str__(self):    #  必须在models中定义str方法
21         return self.name  
22 
23 
24 前端页面同方式一
View Code

 

 

 

Django Form之钩子函数:

为了对Form正则验证后的数据进行进一步的验证:如注册验证(用户是否存在)、登录验证(用户名密码是否正确),使用钩子函数可以完成此类验证

源码分析:

 1 通过验证的is_valid方法的源码,找到full_clean方法如下:
 2     def full_clean(self):
 3         """
 4         Clean all of self.data and populate self._errors and self.cleaned_data.
 5         """
 6         self._errors = ErrorDict()
 7         if not self.is_bound:  # Stop further processing.
 8             return
 9         self.cleaned_data = {}
10         # If the form is permitted to be empty, and none of the form data has
11         # changed from the initial data, short circuit any validation.
12         if self.empty_permitted and not self.has_changed():
13             return
14 
15         self._clean_fields()    # 钩子函数,分别对指定的每一个字段进行验证
16         self._clean_form()        # 查看_clean_form源码,可知调用了全局钩子函数clean()
17         self._post_clean()
18 
19 看_clean_fields私有字段可知:
20     def _clean_fields(self):
21         for name, field in self.fields.items():
22             # value_from_datadict() gets the data from the data dictionaries.
23             # Each widget type knows how to retrieve its own data, because some
24             # widgets split data over several HTML fields.
25             if field.disabled:
26                 value = self.get_initial_for_field(field, name)
27             else:
28                 value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
29             try:
30                 if isinstance(field, FileField):
31                     initial = self.get_initial_for_field(field, name)
32                     value = field.clean(value, initial)
33                 else:
34                     value = field.clean(value)
35                 self.cleaned_data[name] = value                        # 当完成了钩子函数之前的验证后,验证通过的数据会暂时的存放在form.cleaned_data中
36                 if hasattr(self, 'clean_%s' % name):                # 如果有钩子函数
37                     value = getattr(self, 'clean_%s' % name)()        # 执行钩子函数
38                     self.cleaned_data[name] = value                    # 钩子函数验证后的返回值存入cleaned_data
39             except ValidationError as e:                            # 验证失败add_error
40                 self.add_error(name, e)
41 
42                 
43                 
44     def _clean_form(self):
45         try:
46             cleaned_data = self.clean()                # 全局钩子函数
47         except ValidationError as e:
48             self.add_error(None, e)
49         else:
50             if cleaned_data is not None:
51                 self.cleaned_data = cleaned_data                
52                 
53                 
54 注意:
55 1、只能拿自己当前字段值,钩子函数体内应该使用与其函数名匹配的字段
56 2、钩子函数必须要有返回值
57 3、遍历验证字段的时候,前一个字段验证过后,后一个字段的局部钩子函数中也不一定能够用clean_data获取到前一个字段的值,因为前一个字段的值如果验证不通过是不会存放在clean_data中

 

示例:

注册验证:

 1 form.py:
 2 class RegisterForm(forms.Form):
 3 
 4     user = fields.CharField(
 5         error_messages={'xxx': '用户名已存在'},
 6         min_length=2,
 7         required=False,
 8         label='用户名:',
 9         widget=widgets.TextInput(attrs={'class': 'c1'})
10     )
11     pwd = fields.CharField(
12         max_length=12,
13         label='密码:',
14         widget=widgets.PasswordInput(attrs={'class': 'c1'})
15     )
16 
17     email = fields.EmailField(validators=[])
18 
19     def clean_user(self):
20         """
21          正则验证通过后,验证用户是否存在,注册验证
22         :return: 通过后把数据放到cleaned_data中返回
23         """
24         c = models.User.objects.filter(user=self.cleaned_data.get('user')).count()
25         print(self.fields.items(), '-----------------------', self.cleaned_data['user'])
26         print(self.cleaned_data['user'])
27         if not c:
28             return self.cleaned_data['user']
29         else:
30             raise ValidationError('用户名已存在!', code='xxx')
31 
32 
33 
34 
35 views.py:
36 def register(request):
37     if request.method == "GET":
38         obj = RegisterForm()
39         return render(request, 'index.html', {'obj': obj})
40     elif request.method == "POST":
41         obj = RegisterForm(request.POST, initial={'user': request.POST.get('user')})     # 提交验证失败保留用户名
42         obj.is_valid()
43         print(obj.errors)
44         return render(request, 'index.html', {'obj': obj})
45 
46 
47 
48 
49 models.py
50 
51 class User(models.Model):
52 
53     user = models.CharField(max_length=32)
54     pwd = models.CharField(max_length=64)
55 
56 
57 index.html:
58     <form action="/app03/register/" method="post">
59         {% csrf_token %}
60         <p>{{ obj.user.label }}{{ obj.user }}</p>
61         <p>{{ obj.pwd.label }}{{ obj.pwd }}</p>
62 {#    <p>{{ obj.user_type3 }}</p>#}
63          <p><input type="submit" value="提交"></p>
64     </form>
View Code

 

登录验证:

 1 from django import forms
 2 from django.forms import fields
 3 from django.forms import widgets
 4 from app03 import models
 5 from django.forms.models import ModelChoiceField,ModelMultipleChoiceField
 6 from django.core.exceptions import ValidationError
 7 
 8 class RegisterForm(forms.Form):
 9 
10     user = fields.CharField(
11         error_messages={'xxx': '用户名已存在'},
12         min_length=2,
13         required=False,
14         label='用户名:',
15         widget=widgets.TextInput(attrs={'class': 'c1'})
16     )
17     pwd = fields.CharField(
18         max_length=12,
19         label='密码:',
20         widget=widgets.PasswordInput(attrs={'class': 'c1'})
21     )
22 
23 
24 
25     def clean(self):
26         """
27         全局钩子函数clean,能拿到cleaned_data中所有验证通过的数据,对整体进行验证,可以进一步进行登录验证
28         :return:
29         """
30         # c = models.User.objects.filter(user=self.cleaned_data['user'], pwd=self.cleaned_data['pwd']).count()
31         # a = {'user': self.cleaned_data.get('user'), 'pwd': self.cleaned_data.get('pwd')}
32         print(self.cleaned_data,'cleaned_data -----2222222222222222222222222')
33         c = models.User.objects.filter(user=self.cleaned_data.get('user'), pwd=self.cleaned_data.get('pwd')).count()
34         print(c, 'this is c---------')
35         if c:
36             return self.cleaned_data
37         else:
38             raise ValidationError('用户名或密码错误', code='xxx')
39 
40 
41 
42 
43 views.py:
44 
45 from app03.forms import RegisterForm
46 
47 def register(request):
48     if request.method == "GET":
49         obj = RegisterForm()
50         return render(request, 'index.html', {'obj': obj})
51     elif request.method == "POST":
52         obj = RegisterForm(request.POST, initial={'user': request.POST.get('user')})     # 提交验证失败保留用户名
53         obj.is_valid()
54         print(obj.errors)
55         return render(request, 'index.html', {'obj': obj})
View Code

 

1 from django.core.exceptions import NON_FIELD_ERRORS
2 '''
3  {
4     '__all__':[],    # 整体验证clean方法的错误信息放在这里 , 也可以写成NON_FIELD_ERRORS(点击看源码可知代指字符串__all__)
5      "email": [{"code": "required", "message": "This field is required."}],
6   "user": [{"code": "required", "message": "This field is required."}],
7   "pwd": [{"code": "required", "message": "This field is required."}]
8   }
9 '''
clean方法的错误信息

 

 1 小结:
 2 form 验证流程:
 3 1、前端提交数据到后台form
 4 2、循环判断所有字段根据正则表达式(默认或自定义正则)
 5 3、执行字段对应的钩子函数,再判断下一个字段(正则、钩子函数)
 6 4、所有字段循环完毕,最后执行clean钩子函数、post钩子
 7 
 8 客户端POST提交的数据作为我们自定义Form类的参数实例化的得到的form对象可以帮助我们进行表单验证,他验证的流程大致是这样的
 9     循环遍历用户提交的POST表单中的数据,取出存在于自定义的Form类中的字段,比如name,password。
10     将取出的字段按照自定义的Form类中字段的验证规则(包括是否为空、长短、以及正则等)进行验证
11     如果Form类中定义了钩子函数cleaned_字段名,就会调用钩子函数,对指定的字段进行更精细的验证(比如用户登陆)

 

 

 

 

自定义验证规则

1 from django.forms import Form
2 from django.forms import widgets
3 from django.forms import fields
4 from django.core.validators import RegexValidator
5  
6 class MyForm(Form):
7     user = fields.CharField(
8         validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
9     )
方式一
 1 import re
 2 from django.forms import Form
 3 from django.forms import widgets
 4 from django.forms import fields
 5 from django.core.exceptions import ValidationError
 6  
 7  
 8 # 自定义验证规则
 9 def mobile_validate(value):
10     mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
11     if not mobile_re.match(value):
12         raise ValidationError('手机号码格式错误')
13  
14  
15 class PublishForm(Form):
16  
17  
18     title = fields.CharField(max_length=20,
19                             min_length=5,
20                             error_messages={'required': '标题不能为空',
21                                             'min_length': '标题最少为5个字符',
22                                             'max_length': '标题最多为20个字符'},
23                             widget=widgets.TextInput(attrs={'class': "form-control",
24                                                           'placeholder': '标题5-20个字符'}))
25  
26  
27     # 使用自定义验证规则
28     phone = fields.CharField(validators=[mobile_validate, ],
29                             error_messages={'required': '手机不能为空'},
30                             widget=widgets.TextInput(attrs={'class': "form-control",
31                                                           'placeholder': u'手机号码'}))
32  
33     email = fields.EmailField(required=False,
34                             error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
35                             widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
方式二:自定义函数
 1 from django import forms
 2     from django.forms import fields
 3     from django.forms import widgets
 4     from django.core.exceptions import ValidationError
 5     from django.core.validators import RegexValidator
 6  
 7     class FInfo(forms.Form):
 8         username = fields.CharField(max_length=5,
 9                                     validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], )
10         email = fields.EmailField()
11  
12         def clean_username(self):
13             """
14             Form中字段中定义的格式匹配完之后,执行此方法进行验证
15             :return:
16             """
17             value = self.cleaned_data['username']
18             if "666" in value:
19                 raise ValidationError('666已经被玩烂了...', 'invalid')
20             return value
方法三:自定义方法(钩子函数)
 1 from django.forms import Form
 2 from django.forms import widgets
 3 from django.forms import fields
 4  
 5 from django.core.validators import RegexValidator
 6  
 7  
 8 ############## 自定义字段 ##############
 9 class PhoneField(fields.MultiValueField):
10     def __init__(self, *args, **kwargs):
11         # Define one message for all fields.
12         error_messages = {
13             'incomplete': 'Enter a country calling code and a phone number.',
14         }
15         # Or define a different message for each field.
16         f = (
17             fields.CharField(
18                 error_messages={'incomplete': 'Enter a country calling code.'},
19                 validators=[
20                     RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'),
21                 ],
22             ),
23             fields.CharField(
24                 error_messages={'incomplete': 'Enter a phone number.'},
25                 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')],
26             ),
27             fields.CharField(
28                 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')],
29                 required=False,
30             ),
31         )
32         super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,
33                                          **kwargs)
34  
35     def compress(self, data_list):
36         """
37         当用户验证都通过后,该值返回给用户
38         :param data_list:
39         :return:
40         """
41         return data_list
42  
43 ############## 自定义插件 ##############
44 class SplitPhoneWidget(widgets.MultiWidget):
45     def __init__(self):
46         ws = (
47             widgets.TextInput(),
48             widgets.TextInput(),
49             widgets.TextInput(),
50         )
51         super(SplitPhoneWidget, self).__init__(ws)
52  
53     def decompress(self, value):
54         """
55         处理初始值,当初始值initial不是列表时,调用该方法
56         :param value:
57         :return:
58         """
59         if value:
60             return value.split(',')
61         return [None, None, None]
方式四:同时生成多个标签进行验证

 

 

 

初始化数据:

在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。

 1 # Form:
 2 from django.forms import Form
 3 from django.forms import widgets
 4 from django.forms import fields
 5 from django.core.validators import RegexValidator
 6  
 7  
 8 class MyForm(Form):
 9     user = fields.CharField()
10  
11     city = fields.ChoiceField(
12         choices=((1, '上海'), (2, '北京'),),
13         widget=widgets.Select
14     )
15 
16 
17 
18 
19 # Views:
20 from django.shortcuts import render, redirect
21 from .forms import MyForm
22  
23  
24 def index(request):
25     if request.method == "GET":
26         values = {'user': 'root', 'city': 2}
27         obj = MyForm(values)
28  
29         return render(request, 'index.html', {'form': obj})
30     elif request.method == "POST":
31         return redirect('http://www.google.com')
32     else:
33         return redirect('http://www.google.com')
34 
35 
36 
37 
38 # HTML
39 
40 <form method="POST" enctype="multipart/form-data">
41     {% csrf_token %}
42     <p>{{ form.user }} {{ form.user.errors }}</p>
43     <p>{{ form.city }} {{ form.city.errors }}</p>
44  
45     <input type="submit"/>
46 </form>
View Code

 

posted @ 2018-04-14 09:10  whitesky-root  阅读(498)  评论(0编辑  收藏  举报