以前咱们已经学习了HTML中的基本标签——form表单,咱们常用它向后台提交数据,常规的form表单是这样的:html
<form action="" method="post"> <p>用户名:<input type="text" name="user"></p> <p>密码:<input type="text" name="pwd"></p> <P><input type="submit"></P> </form>
可是有许多时候咱们这样使用很是不方便,为何这样说了,上面的form表单中只有两个提交项,前端
可是实际中确定不止两个,若是使用上面的方式,那么一旦有哪个提交项不符合规定,整个form表单都得重写,git
这是后用户确定会抱怨,“凭什么错了一个所有得重写”,因此这样不方便,还有一个就是在对输入的东西值进行校验的时候,正则表达式
每次都得先从前面取值,再在后台校验,烦不烦,能不能在后台生成得时候就设置好了?数据库
这就是jango中form组件能够很是好的解决得问题。下面会一步一步解开解开它得面纱。django
1、在Django中构建一个from表单json
下面建立一个最简单的form表单:数组
from django.shortcuts import render #导入forms组件 from django import forms class LoginForm(forms.Form): user = forms.CharField(max_length=10) pwd = forms.CharField() def login(request): #实例化一个对象 form_obj = LoginForm() return render(request,"login.html",{"form_obj":form_obj})
在前端页面中展现form_obj:浏览器
{{ form_obj.as_p }}
as_p是一个特殊的属性,常见的有:app
{{ form.as_table }}
以表格的形式将它们渲染在<tr>
标签中{{ form.as_p }}
将它们渲染在<p>
标签中{{ form.as_ul }}
将它们渲染在<li>
标签中
下面是效果:
能够看一下前端页面:
能够看到它自动给你生成了两个input标签,可是还缺一个提交按钮,form组件能不能作了,答案是不能,还得你本身写。
刚才展现了一种前端加载得方法,还有另一种比较灵活得方法,不过须要咱们添加更多的标签。
<form action="" novalidate method="post"> {% csrf_token %} <div> <label for="">用户名</label> {{ form_obj.user }} </div> <div> <label for="">密码</label> {{ form_obj.pwd }} </div> <input type="submit"> </form>
获得得效果就是:
貌似如今还看不出来form组件得做用,不急咱们慢慢来。
注意:
(1)novalidate这个属性是关闭浏览器的自动校验。
(2)Django 原生支持一个简单易用的跨站请求伪造的防御。当提交一个启用CSRF 防御的POST
表单时,你必须使用上面例子中的csrf_token
模板标签
2、form组件的两大功能展现
1.保存以前的输入值
def login(request): #若是进行的是提交操做 if request.method == "POST": #再次实例化一个对象,可是与get请求不一样的是,这里form_obj是有值。 form_obj = LoginForm(request.POST) return render(request, "login.html", {"form_obj": form_obj}) #实例化一个对象 form_obj = LoginForm() return render(request,"login.html",{"form_obj":form_obj})
注意:两个form_obj是不同的,一个是带着值进行渲染的,一个是空值,这也是为何提交不会清除掉原来的值得缘由了。
2.校验
Form的实例具备一个is_valid()方法,它会对输入的字段进行校验,若是全部的字段都合法,那么他就会:
在建立类的时候,咱们其实能够给这个属性加上约束,就像这样:
user = forms.CharField(max_length=10,min_length=5) #最大长度为10,最小长度为5,这样就能够校验了
From实例的对象还有一个errors方法,是全部字段全部约束的错误信息,咱们能够将其打印出来:
def login(request): #若是进行的是提交操做 if request.method == "POST": #再次实例化一个对象,可是与get请求不一样的是,这里form_obj是有值。 form_obj = LoginForm(request.POST) if form_obj.is_valid(): return HttpResponse("登陆成功") else: print(form_obj.errors) print(type(form_obj.errors)) print(form_obj.errors["user"]) print(type(form_obj.errors["user"])) return render(request, "login.html", {"form_obj": form_obj}) #实例化一个对象 form_obj = LoginForm() return render(request,"login.html",{"form_obj":form_obj})
下面是打印结果:
print(form_obj.errors) #这是个什么东西咱们不知道 <ul class="errorlist"><li>user<ul class="errorlist"><li>Ensure this value has at least 5 characters (it has 3).</li></ul></li></ul> #打印一下类型 print(type(form_obj.errors)) #能够猜想出它是一个错误字典 <class 'django.forms.utils.ErrorDict'> #既然是字典,那么每一个属性确定就是键了,取出一个键 print(form_obj.errors["user"]) #这是什么咱们也不知道,可是其中有英文报错 #最少5个,你只有3个 <ul class="errorlist"><li>Ensure this value has at least 5 characters (it has 3).</li></ul> #打印一下类型 print(type(form_obj.errors["user"])) #能够看到是一个列表,列表中的是各类约束的报错信息 <class 'django.forms.utils.ErrorList'> #在这里无论有几个报错信息,咱们只取一个就够了,若是还有错误,接着改就是了,这也符合现实状况。 print(form_obj.errors["user"][0]) #成功取出了报错信息 Ensure this value has at least 5 characters (it has 3).
既然这样,在前端就能够显示了:
<div> <label for="">用户名</label> {{ form_obj.user }}<span>{{ form_obj.user.errors.0 }}</span> </div>
显示效果就是:
可是是英文的,可不能够是中文了?固然能够:
在属性的约束中有一个error_messages,可让咱们自行进行约束:
class LoginForm(forms.Form): user = forms.CharField(max_length=10,min_length=5, error_messages={ "min_length":"最小长度为5", "max_length":"最大长度为10", "required":"不能为空", #不能为空 })
结果以下:
能不能跳一跳样式了》这样很差看,这个后面再说,如今还有一个问题,密码问题,
咱们发现输入的密码不能隐藏。
之因此一直没有举例,是由于文本输入有Charfield,并无Passwordfield,在这里须要借助widget这个约束进行密码输入。
先进行导入:
from django.forms import widgets
而后设计类的时候调用:
pwd = forms.CharField( error_messages={ "required":"不能为空", }, widget=widgets.PasswordInput() )
效果以下:
在widgets中有不少限制,不是只有PasswordInput这一个,还有TextInput、CheckboxInput、NumberInput等。
在widgets中还能够进行属性的设置,例如:
pwd = forms.CharField( error_messages={ "required":"不能为空", }, widget=widgets.PasswordInput(attrs={"class":"active","id":"s1"}) )
这样就能够自行设计样式了,在前端查看:
3、高级进阶:钩子的设定
不少时候,咱们想要自行设计校验的手段,该若是作了,Django给咱们提供了钩子这一手段:
先来看一段源码:
if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value
这段源码是可以设置钩子的来源。
#局部钩子 def clean_user(self): val1 = self.cleaned_data.get("user") #若是这个字符串所有都是由数组组成 if not val1.isdigit(): return val1 else: # 注意这个报错信息已经肯定了 raise ValidationError("用户名不能所有是数字组成")
还能够设置全局钩子:
def clean(self): pwd=self.cleaned_data.get("pwd") repeat_pwd=self.cleaned_data.get("repeat_pwd") if pwd==repeat_pwd: print("yes") return self.cleaned_data else: print("no") raise ValidationError("两次密码不一致!")
为何有全局钩子了,由于每个钩子都是和某个具体的字段绑定,只能获取本身的字段值,不能获取其余的值,因此须要全局钩子。
from django.shortcuts import render,HttpResponse # Create your views here. from django import forms from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from django.forms import widgets import re from appo1 import models class LoginForm(forms.Form): user = forms.CharField(min_length=6,max_length=12, error_messages={ "required":"用户名不能为空", "min_length":"最小长度为5", "max_length":"最大长度为12" }, widget=widgets.TextInput(attrs={"id":"s1"}) ) pwd = forms.CharField(min_length=6, error_messages={ "required":"密码不能为空", "min_length":"最小长度为6" } ) repeat_pwd = forms.CharField( error_messages={ "required": "必须输入", } ) email = forms.EmailField( error_messages={ "required": "", "invalid":"格式错误" } ) phone = forms.CharField( error_messages={ "required": "必须输入", } ) #必须以clear起手,不然不能尽心检测 def clean_user(self): import re val1 = self.cleaned_data.get("user") ret = re.findall(r'^_', val1) if not val1.isdigit(): if ret: return val1 else: raise ValidationError("必须如下划线开头") else: raise ValidationError("用户名不能所有为数字组成") def clean_pwd(self): val2=self.cleaned_data.get("pwd") if not val2.isdigit(): return val2 else: raise ValidationError("密码不能所有为数字!") def clean_phone(self): val3 = self.cleaned_data.get("phone") if val3.isdigit(): if len(val3) != 11: raise ValidationError("亲,号码是11位!") else: return val3 else: raise ValidationError("号码必须是数字!") def clean(self): pwd=self.cleaned_data.get("pwd") repeat_pwd=self.cleaned_data.get("repeat_pwd") if pwd==repeat_pwd: print("yes") return self.cleaned_data else: print("no") raise ValidationError("两次密码不一致!") def login(request): if request.method == "POST": form_obj = LoginForm(request.POST) if form_obj.is_valid(): user = request.POST.get("user") pwd = request.POST.get("pwd") email = request.POST.get("email") phone = request.POST.get("phone") ret = models.user_info.objects.create(user=user,pwd=pwd,email=email,phone=phone) return HttpResponse("你好,欢迎回来!") else: ret = form_obj.errors.get("__all__") #{{form_obj.repwd}} < span > {{form_obj.errors.repwd.0}}{{form_obj.non_field_errors.0}} < / span > #还能够在前端直接传 return render(request, "login.html", {"form_obj": form_obj,"ret":ret}) form_obj = LoginForm() return render(request,"login.html",{"form_obj":form_obj}) import json def user_check(request): response = {"is_reg": False} user = request.POST.get("user") ret = models.user_info.objects.filter(user=user) print(ret) if ret: response["is_reg"] = True
4、form组件补充知识
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
在witgits中选择使用
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 # )