Django之form组件
1、介绍
之前form表单提交后,都是自己在前端和后端自定义验证规则,这样只会重复造轮子,不够简洁,所以django封装好了form表单的验证,这就是form组件。
form组件的主要功能:
1. 生成可被渲染的HTML标签
2. 对用户提交的数据经行校验
3. 把校验过后产生的提示信息返回给前端
4. 可以保留上次输入内容
2、使用form组件进行一个简单的验证
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap3/css/bootstrap.min.css" /> <script src="/static/bootstrap3/js/bootstrap.min.js"></script> </head> <body> <form action="" method="post"> <h1>用户登录</h1> <div> <div> <label for="user">用户名</label> <p><input type="text" name="name" id="user"></p> </div> <div> <label for="pwd">密码</label> <p><input type="password" name="pwd" id="pwd"></p> </div> <div> <label for="email">邮箱</label> <p><input type="text" name="email" id="email"></p> </div> <input type="submit"> </div> </form> </body> </html>
视图view.py 文件 from django import forms class UserForm(forms.Form): """定义登录用户form验证规则""" name = forms.CharField(max_length=8) pwd = forms.CharField(max_length=8, min_length=3) email = forms.EmailField() def login(request): if request.method == "GET": return render(request, "login.html") else: name = request.POST.get("name") pwd = request.POST.get("pwd") email = request.POST.get("email") # 进行判断 # 传入的字典的key和UserForm的属性名必须一样 form_obj = UserForm({'name': name, 'pwd': pwd, "email":email}) # form_obj = UserForm(request.POST) if form_obj.is_valid(): # 如果数据全部校验通过才为True否则均为False pompt = "登录成功" else: pompt = "登录失败,格式不对" return HttpResponse(pompt)
3、form组件的方法和属性
# 示例: from django import forms class RegForm(forms.Form): name = forms. CharFiled(max_length=6) pwd = forms.CharField(max_length=8, min_length=3) email = forms.EmailField() # 传值方式: res = RegForm({'name': 'abcdefd', 'pwd': "12", 'email': "123"}) # 要求: # 1. 传入的字典的key和UserForm的属性名必须一样 # 2. 必须是字典格式 # 判断校验是否通过 res.is_vaild() # 结果是True或False # 拿到校验不通过的原因信息 res.errors # 不同版本打印的格式都不一样, # 但都可以认为是字典套列表的格式,所以可以通过字典的get()方式获取 res.errors.get(pwd) # 结果<ul class="errorlist"><li>Ensure this value has at least 3 characters (it has 1).</li></ul> # 向错误字典内添加错误信息 res.add_error('name', '必须大写') print(res.errors.get("name")) # 结果是:<ul class="errorlist"><li>必须大写</li></ul> # 拿到校验通过的数据,必须在res.error或res.is_vaild()后才能拿到,单独取res.cleaned_data报错 res.cleaned_data # 结果:{'name': 'sun'} # 拿到所有数据 res.data # 如果多传了字段,会直接丢弃,不去比较 # 少传字段,校验不会通过
4、使用form组件前端渲染
后端需要把form对象返回当前端
form_obj = UserForm() return render(request, "reg.html", {"form_obj": form_obj})
前端渲染的三种方式:
# 第一种方式: {{ form_obj.as_p }} # 生成p标签, 标签内是input {{ form_obj.as_ul }} # 生成li标签,标签内是input # 第二种方式: <form> <p><label>{{ form_obj.name.label }}</label>{{ form_obj.name }}</P> <p><label>{{ form_obj.pwd.label }}</label>{{ form_obj.pwd }}</P> </form> # 通过后端的字段的参数label="用户名: ",可以把上面变成中文 # form组件只能获取用户输入(或选择的,只要是用户操作的)的标签,按钮必须手动设置 # 第三种: <form> {% for foo in form_obj %} <p> <label>{{ foo.label }}</label> {{ foo }} </p> </form> <form novalidate> 取消浏览器的校验功能
5、表单验证并渲染错误信息
后端实现表单验证 参数1: # 自己指定错误信息 error_messages = { "max_length": "长度最长只能6位", "required": "必须填写用户名" } 其它的key: Email字段:'invalid': '邮箱格式错误' # error_messages里面的key错误,则报错 # ValueError: dictionary update sequence element #0 has length 0; 2 is required # 参数2: # 把密码隐藏,并加入了类名 widget = forms.widgets.PasswordInput(attrs={'class': " c1 form-control"}) # 参数3: # 设置初始值 initial="ywsun" 前端使用方式 {{ foo.errors }} 拿到所有文本,并自动生成一个ul标签 {{ foo.errors.0 }} 只拿到文本
示例:
from django import forms from django.forms import widgets class UserForm(forms.Form): """定义登录用户form验证规则""" name = forms.CharField( max_length=8, error_messages={ "max_length": "用户名长度最长8位", "required": "必须填写用户名", } ) pwd = forms.CharField( max_length=8, min_length=3, error_messages={ "max_length": "用户名长度最长8位", "min_length": "用户名长度最短3位", "required": "必须填写密码", }, widget = widgets.PasswordInput(attrs={"class": "form-control"}), ) email = forms.EmailField( error_messages={ "required": "必须填写邮箱", "invalid": "邮箱格式错误", # ValueError: dictionary update sequence element #0 has length 0; 2 is required } ) def login(request): if request.method == "GET": form_obj = UserForm() return render(request, "login.html", {"form_obj": form_obj}) else: form_obj = UserForm(request.POST) if form_obj.is_valid(): return HttpResponse("OK") else: return render(request, "login.html", {"form_obj": form_obj})
<body> <div> <form method="post" action="" novalidate> {% for foo in form_obj %} <p> <label>{{ foo.label }}</label> {{ foo }}{{ foo.errors.0 }} </p> {% endfor %} <input type="submit" value="提交"> </form> </div> </body>
6、钩子函数(自定义验证规则)
6.1 局部钩子
from django.core.exceptions import ValidationError def clean_name(self): name = self.cleaned_data.get("name") if "aaa" in name: self.add_error("name", "不能包含aaa") # 另一种方法 # raise ValidationError("不能包含aaa") # 拿出的数据必须返回去 return name
6.2 全局钩子
# 全局校验 def clean(self): pwd_value = self.cleaned_data.get("pwd") repwd_value = self.cleaned_data.get("re_pwd") if pwd_value == repwd_value: # 需要把错误加入errors内 self.add_error("repwd_value", "两次密码不一致") # 另一种方式 raise ValidationError({"re_pwd": "两次密码不一致"}) # 全局校验过后,把cleaned_data 把数据返回(也可以不写) return self.cleaned_data
强调:
1. 局部钩子使用raise添加错误信息
原因:如果使用self.add_error("name", "error"), 添加错误信息,最后的cleaned_data内仍然会有name的键值对存在,而 raise ValidationError("error"), 则不会,(和定义的函数有关,使用raise则没有返回值)
2. validationError的使用:
参数可以是字符串,列表,字典(key是字段名)
对于局部钩子:只能使用字符串和列表
对于全局钩子:三者都可以使用,但字符串和列表的错误信息会放在"__all__"下(在前端取不出来错误信息,同时cleaned_data里仍然会有校验不同过的字段),推荐使用字典格式,或self.addr_error()
7、form 组件中常用参数
7.1 Field字段中常用参数(其他的字段都继承了Field)
|
参数
|
说明
|
|
required
|
required=False,表示这个字段可以为空
|
|
widget
|
用来设置input标签的类型
|
|
initial
|
initial="sun",表示input框内的初始值为sun |
|
label
|
label="姓名",表示label标签的内容的内容为"姓名",不能自动生成label标签
|
|
label_suffix
|
label_suffix=">>", 表示label内容的后缀为">>", 单拿通过{{foo. label_tag}}, 拿到label和后缀 |
|
error_messages
|
自定义验证不通过提示信息,error_messges={"参数": "错误提示信息"}
|
|
help_text
|
在标签旁生成帮助信息,单个标签通过{{foo.help_text}} 拿到
|
|
validators=[]
|
使用正则自定义验证规则 |
|
localize=False
|
是否支持本地化(时间和语言)
|
|
disabled=False
|
是否可编辑,Ture即不可编辑
|
# validators 用法: from django.core.validators import RegexValidator name = serializers.CharField( # max_length=3, min_length=2, validators=[RegexValidator(regex='^\d+$', message="必须是数字")], error_messages={ 'max_length': 'must be less than 3 words', 'min_length': 'must be more than 2 words', }, )
7.2 widget 主要用在单选、多选和下拉框
如果需要个性化样式,就得手动添加CSS
1. radio 单选框
from django import forms from django.forms import widgets # 方式1: gender= forms.ChoiceField( choices=choices=((1, "boy"), (2, "girl"), (3, "other")), widget=widgets.RadioSelect() ) # 方式2: gender = forms.IntegerField( widget=widgets.RadioSelect( choices=((1, "boy"), (2, "girl"), (3, "other")), ) )
前端只能通过{{ foo }} 拿到一个label和ul标签,如下: <div><label for="id_gender_0">Gender:</label> <ul id="id_gender" class="radio"> <li> <label for="id_gender_0"><input type="radio" name="gender" value="1" class="radio" required="" id="id_gender_0"> boy </label> </li> <li> <label for="id_gender_1"><input type="radio" name="gender" value="2" class="radio" required="" id="id_gender_1"> girl </label> </li> <li> <label for="id_gender_2"><input type="radio" name="gender" value="3" class="radio" required="" id="id_gender_2"> other </label> </li> </ul> </div>
2. checkbox 单选
keep = forms.ChoiceField( label="是否记住密码", initial="1", widget=forms.widgets.CheckboxInput() ) # 通过设置initial,则选框会value="1",且默认被选中
# 方式2: keep = forms.ChoiceField( label="是否记住密码", widget=forms.widgets.CheckboxInput(attrs={"value": "checked"}) ) # 可以直接设置value属性,而不被选中
得到前端标签 <p> <label for="id_keep">是否记住密码:</label> <input type="checkbox" name="keep" value="1" required="" id="id_keep" checked=""> </p>
3. checkbox 多选框
# 方式一: hobby = forms.MultipleChoiceField( choices=((1, "basketball"), (2, "football"), (3, "PingPang"),), widget=forms.widgets.CheckboxSelectMultiple() ) # 方式二: hobby = forms.IntegerField( widget=forms.widgets.CheckboxSelectMultiple( choices=((1, "basketball"), (2, "football"), (3, "PingPang"),) ) )
前端得到标签 <div><label>Hobby:</label> <ul id="id_hobby"> <li> <label for="id_hobby_0"><input type="checkbox" name="hobby" value="1" id="id_hobby_0"> basketball </label> </li> <li> <label for="id_hobby_1"><input type="checkbox" name="hobby" value="2" id="id_hobby_1"> football </label> </li> <li> <label for="id_hobby_2"><input type="checkbox" name="hobby" value="3" id="id_hobby_2"> PingPang </label> </li> </ul> </div>
4. select 单选框
# 方式1: hobby = forms.ChoiceField( choices=((1, "basketball"), (2, "football"), (3, "PingPang"),), widget=forms.widgets.Select() ) # 方式2: hobby = forms.CharField( widget=forms.widgets.Select( choices=((1, "basketball"), (2, "football"), (3, "PingPang"),) ) )
<div> <label for="id_hobby">Hobby:</label> <select name="hobby" id="id_hobby"> <option value="1">basketball</option> <option value="2">football</option> <option value="3">PingPang</option> </select> </div>
5. select 多选框
# 方式1:
hobby = forms.MultipleChoiceField(
choices=((1, "basketball"), (2, "football"), (3, "PingPang"),),
widget=forms.widgets.SelectMultiple()
)
# 方式2:
hobby = forms.CharField(
widget=forms.widgets.SelectMultiple(
choices=((1, "basketball"), (2, "football"), (3, "PingPang"),)
)
)
6. 解决choice 动态显示数据
使用choices从数据库中获取数据时,由于是静态字段,获取的值无法实时更新, 解决办法:
from django.forms import Form from django.forms import widgets from django.forms import fields class RegisterForm(Form): ... hobby = forms.MultipleChoiceField( # choices=models.Hoppy.objects.values_list("id", "option") widget=forms.widgets.SelectMultiple() ) def __init__(self, *args, **kwargs): super(RegisterForm, self).__init__(*args, **kwargs) self.fields['hoppy'].choices = models.Hoppy.objects.values_list("id", "option")
8、form 的内置字段(类比于model的内置字段)
Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} 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类型

浙公网安备 33010602011771号