day75 form 组件(对form表单进行输入值校验的一种方式)

时间:2023-03-09 04:23:54
day75  form 组件(对form表单进行输入值校验的一种方式)

我们的组件是什么呢

select distinct(id,title,price) from book 

ORM:
model.py class Book():
title=model.CharField(max_length=32) 类名-----表名
类属性---字段
类实例对象---一行记录 单表操作:
添加:
book_obj=Book.objects.create(title="python",price=123)
book_obj=Book(title="python",price=123)
book_obj.save() 查询:
1、 Book.objects.all() # QuerySet() [obj1,obj2,....]
2、 Book.objects.filter(price__gt=100,id__gt=5) # QuerySet() [obj1,obj2,....]
3、 Book.objects.get(title="python") # model对象
4、 Book.objects.exclude()
5、 Book.objects.all().order_by("-price")
6、 Book.objects.all().values("title","id","publish__name") # QuerySet() [] l=[]
for obj in Book.objects.all():
temp={}
temp["title"]=obj.title
temp["id"]=obj.id
temp["publish__name"]=obj.publish.name # QuerySet() []
l.append(temp) 7、 Book.objects.all().values_list() 8、 Book.objects.all().values("title","id","publish__name").distinct()
9、 Book.objects.all().count() #终止子句
10、Book.objects.all().first() model对象
11、Book.objects.all().last() model对象
12、Book.objects.all()[2:5]
13、exist() ret=Book.objects.all().first()
if ret:
pass 删除:
Book.objects.all().delete()
修改:
Book.objects.filter(id=1).update(title="linux") --- 推荐 obj=Book.objects.filter(id=1)[0]
obj.title="linux"
obj.save() day75 1 form组件: (1) 数据重置
(2) 校验规则 ===
流程: form_obj=LoginFrom(): self.fields={"user":"user规则","pwd":"pwd规则对象"}
form_obj=LoginFrom(request.POST): self.fields={"user":"user规则","pwd":"pwd规则对象"} is_valid:
self._errors={}
self.cleaned_data = {} self._errors={"user":'',"__all__":''}
self.cleaned_data = {"pwd":""} '''
实例化时: self.fields={
"username":"字段规则对象",
"password":"字段规则对象", } is_valid时: self._errors = {}
self.cleaned_data = {} #局部钩子: for name, field in self.fields.items():
try: value = field.clean(value)
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e) # 全局钩子: self.clean() # def self.clean():return self.cleaned_data return not self.errors # True或者False ''' 作业:
基于form组件实现注册页面 user : 用户名是否注册,用户名长度
pwd
repeat-pwd
email
tel 2 auth --- django的认证系统 3 ajax参数 文件上传 4 中间件

就是一个表单,一个form表单,前端模板标签里面的form标签,构成的form表单,

但是它的功能不仅仅是局限于form表单,

我们把应用场景举例说明就会一目了然了

我们在很多的网页上面提交注册信息的时候,我们通常都需要输入很多的input框才可以提交最下面的注册按钮,然后这些input框由于内容过多,不小心就会输错,比如格式不正确,不符合要求就无法提交成功,此时我们只要有一个input框输错了都会刷新页面,然后所有的数据都会清空了,这其实是很不科学的,

我们应该是在提交的时候就给我们验证出来哪些是有错误的信息需要重新提交的,会有明显标识出来,然后我们的用户就只需要把错误的input框给改过来即可,

但是我亲身经历的,应该是*的一个破网站,他们都没有专业的人做这些,都是外包的,外包的产品是没有质量可言的,都是赶产量,粗糙至极,所以就真的有那样的网页,在注册的时候需要反复地重新提交所有的input框的信息,简直是噩梦一场,学了form组件才知道,那样的网页是有bug的,有极大的可优化空间的,

我们的form组件可以获取到你输入到input框里面的值,然后在你提交信息的时候,把你输入有误的信息在页面上标记出来,并且告诉你为什么有误,然后那些没有问题的input框里面的值还是保留你自己输入过的内容,这样一来我们输入的那些没有问题的信息还是留在页面上,这是一个小细节问题,但是通过代码实现,让用户尽可能简单地使用,我们的后台代码就需要做更多更复杂的操作去实现

我们的form组件就是干这个事的.

构建一个表单

我们所需要的form模板:

<form action="/your-name/" method="post">
<label for="your_name">your name:</label>
<input id="your_name" type="text" name="your_name">
<input type="submit" value="ok">
</form>

我们以上是简单的表单,实际应用中,一个表单可以包含几十个字段,其中大部分需要预填充,而且我们预料到用户将来回编辑-提交几次才能完成操作.

我们可能需要在表达提交之前,在浏览器端做一些验证,我们可能想使用非常复杂的字段,以允许用户做类似从日历中挑选日期这样的事情,等等.

这个时候,让django来为我们完成大部分工作是很容易的

有两个突出的优点:

1.form表单提交时,数据出现错误,返回的页面中仍可以保留之前输入的数据,

2.方便地限制字段条件

在django中构建故意表单

form类

我们在django中HTML表单起始点是这里

# forms.py

from django import forms

class NameForm(forms.Form):

  your_name=forms.charField(label='your name',max_length=100)

它定义一个form类,只带有一个字段(your_name)

字段允许的最大长通过我们的max_length定义,它完成两件事,首先它在HTML的input标签上放置一个maxlength="100"[我们的浏览器根据这里的属性第一时间阻止用户输入多于这个数目的字符]它还意味着当django收到浏览器发送过来的表单时,它将验证数据的长度.

form的实例具有一个is_valid()方法,它为所有的字段运行验证的程序,当调用这个方法时,如果所有的字段都包含合法的数据,它将

返回True

将表单的数据放到cleaned_data属性中.

我们的form组件两种创建方式:

第一种方式:

<form action="" method="post">  # 我们action如果不写内容的话,它默认就是直接提交到当前的网址

{% csrf_token %}

{{form_obj.as_p}}  # 这里是直接创建p标签同时它里面包着input标签这都是自动生成的,就这里的一句代码就可以生成这些

<input type=submit> # 这里我们是自己加上去的它不会生成submit也不会生成form表单

</form>

# 这一种方式更加的便捷,它是给我们固定好了生成p标签去包裹input,我们有几个字段就有几个input,生成后自动调整格式,padding之类的,会看起来更加整洁,但是由于是固定好的,所以可变性不够强

第二种方式:

<form action="" novalidate method="post">

{%csrf_token%}

<div>

<label for="">用户名</label>{{form_obj.user}}  # 这里的语法是自动生成input框,我们需要自己手动加上其他的标签,我们可以自定义使用label或者div等等我们自己定义的标签去包裹它,

<label for="">密码</label>{{form_obj.pwd}}

我们这里的方法灵活性更强,但是padding等等一些css需要自己另外去定义,否则很难看

第三种方式:

使用for循环,我们可以把我们的类里面的属性都使用for循环遍历出来,每一个使用for循环自动生成的标签里面都会有一个label属性,这个label属性我们在views视图函数里面的类属性里面直接设置自定义值,就跟我们的max_length=20一样的格式,

然后我们在for循环里面还可以加上if判断,就是我们的模板语言的语法

还没写完.............................................................我们的form组件里面有源码需要去了解,这个源码里面是各种函数和类之间的互相调用互相跳转,需要整理笔记,把跳转的过程记录下来

完整的表单,第一次渲染时,看上去像:

<label for="yourname">name:</label>
<input id="yourname" type="text" name="your_name" maxlength="">

它自动生成的这样不包含form表单,以及提交按钮,我们必须自己在模板中提供它们.

视图:

发送给django网站的表单数据通过一个视图处理,一般和发布这个表单的是同一个视图,这允许我们重用一些相同的逻辑.

当处理表单时,我们需要在视图中实例化它:

定义的类:

from django.forms import widgets

class NameForm(forms.Form):
user=forms.CharField(max_length=12,min_length=5,error_messages={"required":"不能为空","min_length":"最小长度为5","max_length":"最大长度为15"}) pwd=forms.CharField(error_messages={"required":"不能为空","invalid":"格式错误"}), # 我们的键对值里面的key的值是固定的写法,不可以自动改变,否则浏览器无法辨识出来 widget=widgets.PasswordInput(attrs={"class":"active abc","egon":""}) # 我们的widget是魔法组件,它可以改变我们的form组件里面自动生成的标签里面的属性,也支持给那些标签添加属性
# views.py
from django.shortcuts import render
fromdjango.http import HttpresponseRedirect from .forms import NameForm # 我们新建了一个文件夹,跟我们的views.py是同一级的目录,然后引入它,使用它里面定义的那个类 def get_name(request):
# if this is a post request we need to process the form data
if request.method == "POST":
# creat a form instance and populate IT with data from the request:
form = NameForm(request.POST) # 我们需要去调用那个类NameForm,然后传参
# check whether it's valid:
if form.id_valid():
# process the data in form.cleaned_data as required
#...
# redirect to a new url:
return HttpResponseRedirect('/thanks/')
# if a get(or any other method) we'll create a blank form
else:
form=NameForm() # 这里也是我们的类实例化出来的对象
return render(request,'name.html',{'form':form})

如果访问的视图是一个get请求,它将创建一个空的表单实例,并将它放置到要渲染的模板的上下文中,这是我们在第一个访问该url是预期发生的情况.

如果表单的提交是用post请求,那么视图将再次创建一个表单实例并使用请求中的数据填充它,form=NameForm(request.POST),这叫做"绑定数据至表单"(它现在是一个绑定的表单)

我们调用表单的is_valid()方法,如果它不为True,我们将带着这个表单返回到模板

这时表单不再为空,(未绑定),所以HTML表单将用之前提交的数据填充,然后可以根据要求编辑并改正它

如果is_valid()为True,我们将能够在cleaned_data属性中找到所有合法的表单数据

在发送HTTP重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其他的处理.

模板:

我们不需要再name.html模板中做很多工作,最简单的例子是:

<form action="/your_name/" method="post">

{% csrf_token %}

{{form}}

<input type="submit" value="Submit">

</form>

根据{{form}},所有的表单字段和它们的属性将通过django的模板语言拆分成HTML标记.

注:django原生支持一个简单易用的跨站请求伪造的防护,当提交一个启用CARF防护的post表单时,你必须使用上面的例子中csrf_token模板标签

现在我们有了一个可以工作的网页表单,它通过django Form描述,通过视图处理并渲染成一个HTML<form>

django form类详解

绑定的未绑定的表单实例

绑定的和未绑定的表单之间的区别非常重要:

未绑定表单没有关联的数据,当渲染给用户时,它将为空,或包含默认值,

绑定的表单具有提交的数据,因此可以用来检验数据是否合法,如果渲染一个不合法的绑定的表单,它将包含内联的错误信息,告诉用户如何纠正数据

字段详解:

#forms.py

from django import forms

class RegisterForm(forms.Form):
username = forms.CharField(max_length=100,
error_messages={"min_length":"最短为5个字符","required":"该字段不能为空"},
)
password = forms.CharField(max_length=100,
widget=widgets.PasswordInput(attrs={"placeholder":"password"})
) telephone=forms.IntegerField(
error_messages={
"invalid":"格式错误"
} ) gender=forms.CharField(
initial=2,
widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
) email = forms.EmailField()
is_married = forms.BooleanField(required=False)

widgets

from django.forms import widgets  # 先引入widgets模块它可以帮我们改变input框的属性,我们的form组价自动生成的input框type属性都是默认的text属性,我们需要在特殊的框里面里面进行修改

就需要使用到它,

每个表单子弹都有一个对应的widget类,它对应的HTML表单widget,例如<input type="text">

在大部分情况下,字段都具有一个合理的默认widget,例如默认情况下,charfield具有一个textinput widget 它在HTML中生成一个<input type="text">

字段的数据

不管表单提交的是什么数据,一旦通过调用is_valid()成功验证(is_valid() 返回True) ,验证的表单数据将位于form.cleaned_data字典中,这些数据已经为你转换好为python的类型

此时,你依然可以从request.post 中直接访问到未验证的数据,但是访问验证后的数据更好一些,

在上面的联系表单示例中,is_married将是一个布尔值,类似地,integerField和floatField字段分别是将值转换为python的int和float

使用表单模板

你需要做的就是将表单实例放进模板的上下文,如果你的表单在context中叫做form,那么{{form}}将正确地渲染它的<label>和<input>元素

表单渲染的选项

对于<label>/<input>对还有几个输出选项:

{{form.as_table}}以表格的形式将它们渲染在<tr>标签中

{{form.as_p}} 将它们渲染在<p>标签中

{{form.as_ul}}将它们渲染在<li>标签中

你必须要自己提供<table>或<li>标签中

{{ form.as_p }}会渲染如下

    <p>
<label for="id_username">Username:</label>
<input id="id_username" maxlength="" name="username" type="text" required="">
</p>

手工渲染字段:

我们没有必要非要让django来分拆表单的字段;如果我们喜欢,我们可以手工来做(例如,这样允许重新对字段排序),每个字段都是表单的一个属性,可以使用{{form.name_of_field}} 访问,并将在django模板中正确地渲染,例如

<div class="fieldWrapper">
{{ form.Username.errors }}
{{ form.Username.label_tag }}
{{ form.Username }}
</div>

渲染表单的错误信息

1,

registerForm=RegisterForm(request.POST)
print(type(registerForm.errors)) #<class 'django.forms.utils.ErrorDict'>
print(type(registerForm.errors["username"])) #<class 'django.forms.utils.ErrorList'>

2,

<ul class="errorlist">
<li>Sender is required.</li>
</ul>

form组件在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类型
...

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

常用选择插件:

# 单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
# )

我们使用我们的form组件实现一次用户信息的注册,代码如下:

后端代码:

继承的class类单独存在的一个py文件里面:

from django import forms
from django.forms import widgets
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
from . models import UserInfo class LoginForm(forms.Form):
name = forms.CharField(label="姓名", max_length=20, min_length=3,
error_messages={"required": "不能为空",
"max_length": "最大长度为20",
"min_length": "最小长度为8"})
pwd = forms.CharField(
label="密码",
max_length=15,
min_length=3,
widget=widgets.PasswordInput(attrs={"class": "active"}),
error_messages={"required": '不能为空', "max_length": "最大长度为15", "min_length": "最小长度3"},
)
re_pwd = forms.CharField(
# label="确认密码",
max_length=15,
min_length=3,
widget=widgets.PasswordInput(attrs={"class": "active"}),
error_messages={"required": "不能为空", "max_length": "最大长度为15", "min_length": "最小长度为3"},
)
email = forms.EmailField(label="邮箱", error_messages={"invalid": "格式错误", "required": "不能为空"})
tel = forms.IntegerField(label="电话", error_messages={"required": "不能为空"})
# 我们钩子的存在就是为了完善校验的功能,我们的上面的类实例化的对象的类里面的校验方式不够完善,还有一些需求无法满足,所以就需要我们在校验的时候使用一些更加具体的要求去拦截,
# 我们自己定义的钩子就是在做这个事情的.
# 局部钩子,
def clean_name(self):
name1 = self.cleaned_data.get('name') # 我们这里的cleaned_data里面的数据是按照上面我们的类里面的属性从上往下依次加载的,
# 以此为例从第一个name开始校验,先校验我们的属性里面的条件,如果符合条件再走我们的自定义局部钩子里面的程序开始校验,
# 完了之后再从头接着我们的name后面的属性开始校验,按照之前的顺序依次走过来,所以我们的校验顺序是要首先满足我们的属性里面的条件,
# 才能走到我们的自定义钩子里面的校验条件
# password = self.cleaned_data.get("pwd")
obj = UserInfo.objects.filter(name=name1)
if obj:
raise ValidationError("该用户已注册") # 这里需要主动触发异常而不是return返回值,我们的模板语言里面是使用的form里面的
# 内置errors的变量语法,这里写成主动触发异常就可以把数据传给我们的前端模板
else:
return name1 # 局部自定义钩子
def clean_tel(self):
val = str(self.cleaned_data.get("tel"))
import re
# ret = re.findall('^(13|14|15|17|18)[0-9]{9}$', val) # 我们的正则匹配里面都是匹配的字符串,
ret = re.findall('^1[34578]\d{9}$', val) # 我们的正则匹配里面都是匹配的字符串,
# 我们如果电话号码是数字类型的话需要把它转换成字符串类型然后再传入到我们的正则里面去,否则页面无法显示,
if ret:
return val
else:
raise ValidationError("格式错误")
  # 全局钩子
def clean(self):
pwd1 = self.cleaned_data.get("pwd")
pwd2 = self.cleaned_data.get("re_pwd")
if pwd1 == pwd2:
return self.cleaned_data
else:
raise ValidationError("两次密码不一致,请重新输入")

views:

from django.shortcuts import HttpResponse, render
from .form_model import LoginForm
from landing import models # 组件作业
def login(request):
if request.method == "POST":
name = request.POST.get("name")
pwd = request.POST.get("pwd")
get_li = LoginForm(request.POST)
# print(request.POST)
if get_li.is_valid():
models.UserInfo.objects.create(name=name, pwd=pwd)
return HttpResponse("you are great")
else:
# all_error = get_li.errors.get("__all__") # 我们这里需要把这个值取出来传给我们的前端模板然后在浏览器就可以显示出来了
# print(all_error)
# return render(request, 'formlist/login.html', {'li': get_li, "all_error": all_error})
return render(request, 'formlist/login.html', {'li': get_li})
form_obj = LoginForm()
return render(request, 'formlist/login.html', {"li": form_obj})

前端页面:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
</head>
<body>
<h1>注册页面</h1> {#<form action="" method="post" novalidate>#}
{# {% csrf_token %}#}
{# 三种方式使用我们的form组件创建form表单#} {# 第一种方式创建我们的form组件#}
{# {{ li.as_p }}#}
{#第二种方式,自己写一个一个写,就是input标签使用系统给我们自动生成的,这样灵活性更加高#}
{# <div>#}
{# <label>用户名</label>{{ li.name }}<span class="sp">{{ li.errors.name.0 }}</span>#}
{# </div>#}
{# <div><!---->#}
{# <label for="">密码</label>{{ li.pwd }}<span class="sp">{{ li.errors.pwd.0 }}</span>#}
{# </div>#}
{# <div><!---->#}
{# <label for="">确认密码</label>{{ li.re_pwd }}#}
{#<span>{{ li.errors.re_pwd.0 }}</span><span>{{ all_error.0 }}</span>#} {# <label for="">确认密码</label>{{ li.re_pwd }}<span#}
{# class="sp">{{ li.errors.re_pwd.0 }}</span><span>{{ li.non_field_errors.0 }}</span>#}
{# </div>#}
{# 我们在前端还有更加便捷的方法可以取值,直接使用我们的non_field_errors.0 就可以取到我们的结果然后再前端直接显示即可不用在后端赋值给另一个变量再传过来了#}
{# <div>#}
{# <label for="">邮箱</label>{{ li.email }}<span class="sp">{{ li.errors.email.0 }}</span>#}
{# </div>#}
{# <div><!---->#}
{# <label for="">电话</label>{{ li.tel }}<span class="sp">{{ li.errors.tel.0 }}</span>#}
{# </div>#}
{# <input type="submit" value="put on">#}
{#</form>#} {# 第三种方式使用for循环去写#} <form action="" novalidate method="post">
{% csrf_token %}
{% for field in li %}
<div><label for="">{{ field.label }}</label>
{{ field }} <span class="sp">{{ field.errors.0 }}</span>
{% if field.label == 'Re pwd' %}
<span> {{ li.non_field_errors.0 }} </span>
{# <span>{{ all_error.0 }}</span> {# 需要加上索引值,取一个数据,否则error得到的是一组数据 #}
{% endif %}
</div>
{% endfor %}
<input type="submit">
</form> <script src="/static/js/jquery-3.2.1.min.js"></script>
{#定时器#}
{#<script>#}
{# function op() {#}
{# $(".sp").each(function(){#}
{# $(this).text("");#}
{# });#}
{# };#}
{# setTimeout("op()",3000);#}
{#</script>#}
</body>
</html>