Django的Form组件
Form 组件
Form 组件的介绍
Form组件可以做的几件事 :
1. 用户请求数据验证
2. 自动生成错误信息
3. 打包用户提交的正确信息
4. 如果其中有一个错误了,其他的正确,保留上次输入的内容
5. 自动创建input标签并可以设置样式
Form组件的使用
校验字段功能
form.is_valid() #校验数据是否正确,正确--True, 有错误信息--false form.cleaned_data #校验数据正确的放这里,以字典形式{"字段":"信息"} form.errors #校验数据错误的信息,{"字段":"错误的信息"} 比如 {"pwd":"不能为纯数字"}
比如 ; 注册用户 示例 :
模型 : models.py
class UserInfo(models.Model): name=models.CharField(max_length=32) pwd=models.CharField(max_length=32) email=models.EmailField() tel=models.CharField(max_length=32)
模板 : register.html :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> {% csrf_token %} <div> <label for="user">用户名</label> <p><input type="text" name="name" id="name"></p> </div> <div> <label for="pwd">密码</label> <p><input type="password" name="pwd" id="pwd"></p> </div> <div> <label for="r_pwd">确认密码</label> <p><input type="password" name="r_pwd" id="r_pwd"></p> </div> <div> <label for="email">邮箱</label> <p><input type="text" name="email" id="email"></p> </div> <input type="submit"> </form> </body> </html>
视图函数 : views register
# forms组件 #引入widets魔法工具包 from django.forms import widgets #页面渲染带有type=text的input标签,添加 class="form-control"的属性 wid_01=widgets.TextInput(attrs={"class":"form-control"}) wid_02=widgets.PasswordInput(attrs={"class":"form-control"}) mas={"required":"该字段不能为空"} class UserForm(forms.Form): #必须继承Form name=forms.CharField(max_length=32, widget=wid_01, label="用户名", #设置为中文的用户名显示 error_messages=msg ) pwd=forms.CharField(max_length=32, widget=wid_02, label="密码", error_messages=msg) r_pwd=forms.CharField(max_length=32, widget=wid_02, label="确认密码", error_messages=msg) email=forms.EmailField(widget=wid_01, error_messages={"invalid":"邮箱格式错误"}, label="邮箱" ) tel=forms.CharField(max_length=32,widget=wid_01) from app01.models import UserInfo from django.shortcuts import render,HttpResponse def register(request): if request.method=="POST": #数据校验 form=UserForm(request.POST) if form.is_valid(): #开始验证 print(form.cleaned_data) # 所有干净的字段以及对应的值,字典形式{"name":"asd","pwd":12345,......} UserInfo.objects.create(**form.cleaned_data) #打散插入数据库 else: print(form.cleaned_data) # print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]} print(form.errors.get("name")) # ErrorList ["错误信息",] errors=form.errors #错误信息 return render(request, "register.html",locals()) #渲染但页面 else: form=UserForm() return render(request,"register.html",locals())
渲染标签功能
方式一 :
缺点 ; 不能调节样式,单一
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!-- 最新版本的 Bootstrap 核心 CSS 文件 --> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <h3>注册页面</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-lg-offset-3"> <form action="" method="post"> {% csrf_token %} <div> <label for="">用户名</label> {{ form.name }} </div> <div> <label for="">密码</label> {{ form.pwd }} </div> <div> <label for="">确认密码</label> {{ form.r_pwd }} </div> <div> <label for=""> 邮箱</label> {{ form.email }} </div> <input type="submit" class="btn btn-default pull-right"> </form> </div> </div> </div> </body> </html>
方式二 :
特点 : 代码量少.方便,可以修改样式,(推荐使用)
<form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} #循环form每个字段,field就是= form.name form.pwd... <div class="form-group"> <label for="">{{ field.label }}</label> {{ field }} <span class="error">{{ field.errors.0 }}</span> #深度查询,.就可以 {% if field.label == "确认密码" %} #获取错误信息 <span class="error">{{ g_error|default:"" }}</span> #确认密码如果 {% endif %} </div> {% endfor %} <input type="reset"> <input type="submit" class="btn btn-success pull-right"> </form>
方式三 ;
特点 : 样式单一 ,但是代码量少
<form action="" method="post"> {% csrf_token %} {{ form.as_p }} <input type="submit" class="btn btn-default pull-right"> </form>
显示错误与重置输入信息功能
视图
def register(request): if request.method=="POST": form=UserForm(request.POST) if form.is_valid(): print(form.cleaned_data) # 所有干净的字段以及对应的值 else: errors=form.errors #获取错误信息 print(form.cleaned_data) # print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]} print(form.errors.get("name")) # ErrorList ["错误信息",] return render(request,"register.html",locals()) form=UserForm() return render(request,"register.html",locals())
模板
<form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} <div> <label for="">{{ field.label }}</label> {{ field }} <span class="pull-right" style="color: red">{{ field.errors.0 }}</span> </div> {% endfor %} <input type="submit" class="btn btn-default"> </form>
局部钩子和全局钩子
from django.shortcuts import render,HttpResponse # Create your views here. from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from django import forms # class BookForm(forms.Form): # title=forms.CharField(max_length=32) # price=forms.IntegerField() # email=forms.EmailField() from django.forms import widgets class UserForm(forms.Form): msg={"required":"该字段不能为空"} user=forms.CharField(min_length=5, label="用户名", error_messages=msg, widget=widgets.TextInput(attrs={"class":"form-control"}) ) pwd=forms.CharField(error_messages=msg, label="密码", min_length=5, widget=widgets.PasswordInput(attrs={"class":"form-control"}) ) r_pwd = forms.CharField(error_messages=msg, min_length=5, label="确认密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}) ) email=forms.EmailField(error_messages={"invalid":"邮箱格式错误"}, label="邮箱", widget=widgets.EmailInput(attrs={"class":"form-control"}) ) def clean_user(self): val=self.cleaned_data.get("user") ret=UserInfo.objects.filter(user=val).first() if not ret: return val else: raise ValidationError("用户名已存在!") def clean_pwd(self): val=self.cleaned_data.get("pwd") if val.isdigit(): raise ValidationError("密码不能是纯数字!") else: return val # 全局钩子 def clean(self): pwd=self.cleaned_data.get("pwd") r_pwd=self.cleaned_data.get("r_pwd") if pwd and r_pwd: #全局钩子里没有错误值就会返回null if pwd==r_pwd: return self.cleaned_data else: raise ValidationError("两次密码不一致!") else: return self.cleaned_data from app01.models import UserInfo def reg(request): if request.method=="POST": print(request.POST) # 数据校验 form=UserForm(request.POST) if form.is_valid(): print(form.cleaned_data) UserInfo.objects.create(**form.cleaned_data) return HttpResponse("OK") else: # print(form.cleaned_data) # print(form.errors) # {"user":["",]} # print(form.errors.get("user")[0]) # {"user":["",]} errors=form.errors #错误信息先经过中间件过滤,正确在走局部钩子校验,这里是手机全局变量的错误. print("------>",form.errors.get("__all__")) if form.errors.get("__all__"): g_error=form.errors.get("__all__")[0] return render(request, "reg.html",locals()) else: form=UserForm() return render(request,"reg.html",locals())
视图 :
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Title</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> .error{ color: red; margin-left: 12px; } </style> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="">{{ field.label }}</label> {{ field }} <span class="error">{{ field.errors.0 }}</span> {% if field.label == "确认密码" %} <span class="error">{{ g_error|default:"" }}</span> {% endif %} </div> {% endfor %} <input type="reset"> <input type="submit" class="btn btn-success pull-right"> </form> </div> </div> </div> </body> </html>
总结 :
1.局部钩子 : 不一定执行,如果第一层校验就被检查出错误,就不进入局部钩子.
2.全局钩子( def clean(self): ) : 一定会走,就算第一层被干掉,相当于是空,所有取值 .get ,就算没有内容,返回null.
Form 组件归类
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
1、Django内置字段如下:
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类型 ...
2 . Django 内置插件如下 :
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
3 . 常用选择插件 :
# 单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 # )