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
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})
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>
方式二:
通过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 前端页面同方式一
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>
登录验证:
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})
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 '''
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>