一些基本概念
HTML表单
在HTML中,表单的作用是收集 HTML 特殊标签的内容,
就是在 <form> ... </ form>
标签中间可以由访问者添加类似于文本,选择或者一些控制模块等等。然后这些内容将会被送到服务端
某些表单的元素 - 文本输入和复选框 - 非常简单而且内建于HTML本身。其它的表单会复杂些; 例如弹出一个日期选择对话框的界面,允许你移动滚动条的界面,使用JavaScript和CSS以及HTML表单<inpt/>
元素来实现操作控制的界面。
一个有效的表单必须指定两样东西:
目的地:响应用户输入数据的URL,就是数据要提交到哪儿
方式:发送数据的HTTP方法,如: POST
GET
等方法
Django Form
可以实现下列功能:
- 验证前端表单发送的请求数据,并返回错误信息,错误信息内容支持自定制。
- 生成前端表单 HTML 相关标签,并且自定义生成表单标签的属性。
- 保留表单上用户上次输入的内容。
- 添加表单的默认值。
验证表单数据
==处理表单时只会用到 GET
和 POST
方法
表单系统的核心部分是Django的 Form 类。
Django的 Model 描述一个对象的逻辑结构,行为以及展现给我们的方式,与此类似,Form 类描述一个表单并决定它是如何工作和展现的。
就像 Model 类的属性映射到数据库的字段一样,表单类的字段会映射到 HTML 的 <input>
表单的元素。( ModelForm 通过一个表单映射模型类的字段到HTML表单的 <input>
元素;
Django的Admin站点就是基于这个实现的)。
基本的流程:
-
导入 forms
在 views.py 文件中,或者在项目的其他你认为规范的任意文档中导入
from django import forms
- 创建一个自定义的类,必须继承 forms.Form 比如创建一个名字叫 AddProductForm 的类
class AddProductForm(forms.Form):
pass
- 在自定义的 Form 类里定义需要验证的字段名及相关属性
表单的字段本身也是类,它们管理表单的数据并在表单提交时进行验证。
Django 的 DateField 和 FileField 处理的数据类型都是属于对象,必须完成不同的处理。
这里的每个字段都对应了一个正则表达式,用来处理字段对应的数据。
这里的字段名必须和前端页面里的 input 框内的 <mark style="box-sizing: border-box;">name 属性的值一致</mark>。
<mark style="box-sizing: border-box;">并且要和数据库中的字段名一致</mark>,这样有利于对接收到的数据进行处理,后面的例子中会有。-
这里<mark style="box-sizing: border-box;">设置的字段数量也必须和前端表单中的标签元素数量一样</mark>。少了,Django 会因收不到数据
而永远对少了的字段通过不了验证;多了,无法对多出的数据其进行验证。
定义一个字段是利用 forms 里的 fields 类,进行实例话一个对象实现的。
利用一个简单的小示例来说明一下:
class AddProductForm(forms.Form): prod_name = forms.CharField( required=True, )
- prod_name 就是
<input name="prod_name"/>
标签类的 name 属性的值 "prod_name",同时也应该对应数据库中名为 : prod_name 的字段 - fields.CharField 表示这是一个字符字段,接受到的数据类型是字符。当然还有其他类型,后面会专门介绍。
- required=True 表示此字段是必填项,这也是默认的值。False 是可以为空。
这样一个对字符类型的数据进行验证的表单字段就创建完了。
- prod_name 就是
- 使用刚才创建的 Form 实例,在视图函数中使用刚才创建的 Form 实例对数据进行验证
# 首先实例化一个对象。要对数据进行验证,当然是需要 POST 方法了,所以把 request.POST 对象直接传进去
form_obj = AddProductForm(request.POST)
# form_obj.is_valid 方法会返回是否验证通过,通过为 真,不通过为假
# form_data = obj.cleaned_data 就是验证通过后的数据
if form_obj.is_valid():
print("验证通过后的数据:",form_obj.cleaned_data)
# 因为刚才我们强调的字段的名字要和数据库中的字段名字一致,所以我们可以之间把清理过的数据存入到数据库中。
models.ProductionLine.objects.create(**form_obj.cleaned_data)
return redirect("/production_info/")
else:
# 验证失败,给前端返回错误信息。
return render(request,'add_production_form.html',{'form_obj':form_obj})
-
前端接收返回的错误信息
所用的错误信息都在 form_obj 的实例化对象中的 errors 对象中
errors 对象的值实际上是一组 HTML 标签和内容
<ul class="errorlist"> <li>name # 每个 li 对应了一个 字段 <ul class="errorlist"> <li>This field is required.</li> </ul> </li> <li>line_number <ul class="errorlist"> <li>This field is required.</li> </ul> </li> <li>date <ul class="errorlist"> <li>日期不能为空</li> </ul> </li> </ul>
HTML 部分
form_obj.字段名.0 会取到字段的错误信息中的字符串内容
<form action="{% url 'add' %}" method="post"> <div class="edit_window"> 业务名称 <input type="text" name="pord_name" value=""> <span>{{ form_obj.errors.name.0 }}</span> </div> <div class="edit_window"> 业务线 <input type="text" name="line_number"> <span>{{ form_obj.errors.line_number }}</span> </div> <div class="edit_window"> 上线日期 <input type="text" name="date" value=""> <span>{{ form_obj.errors.date }}</span> </div> <div class="edit_window"> <input class=".btn" type="submit" value="提交"> <input class=".btn-lg" type="button" value="取消"/> </div> </form>
前端显示的错误信息效果
[图片上传失败...(image-315baf-1561038656751)]
基本验证的流程就是这样子的。
forms 字段的类型
这里仅介绍一些常用的,更多的请参考: Django 官网
- CharField 字符串 (普通文字) EmailField 字符串(邮箱格式) IntegerField 字符串(数字格式) GenericIPAddressField 字符串(IP格式,v4和v6) FileField 文件对象
- DateField 时间对象 RegexField 字符串(自定义) ChoiceField 多选
定制错误信息
Form 类代码:
class AddProductForm(forms.Form):
name = forms.CharField(
min_length =3, # 可以定义字符串的最小和最大长度
max_length =18,
error_messages={
'required': '业务名称不能为空', # 自定义错误信息
'min_length': '业务名不能小于3个字符',
# 'invalid': "无效的名称",
},
)
自动生成自定制的表单给前端,并且可以保留用户上次输入的内容
Form 类代码:
class AddProductForm(forms.Form):
name = forms.CharField(
required=True,
# 自动生成 HTML 标签,并用 attrs 指定标签属性
widget=widgets.TextInput(attrs={'class':'form-control', "k": "v"),
)
视图代码:
GET
实例化一个 Form 对象,不传任何参数,创建的对象就会有自动生成的 HTML 标签,返回给前端即可。
<mark style="box-sizing: border-box;">这种情况下,表单里并没有绑定任何数据,此时前端看到的表单里是空内容</mark>。
POST
实例化一个 Form 对象时,传入一个或者多个数据对象,就会把数据绑定到表单上。
此时再返回给前端,前端就会保留上次输入的信息了
def add_production(request):
if request.method == "GET":
# 实例化一个 Form 对象,不传任何参数,创建的对象就会有自动生成的 HTML 标签,返回给前端即可。
# 这种情况下,表单里并没有绑定任何数据,此时前端看到的表单里是空内容。
form_obj = AddProductForm()
return render(request, 'add_production_form.html', {'form_obj': form_obj})
elif request.method == "POST":
form_obj = AddProductForm(request.POST)
return render(request, 'add_production_form.html', {'form_obj': form_obj})
HTML 代码:
<form action="{% url 'add' %}" method="post" novalidate>
{% csrf_token %}
<div class="form-group">
<label>业务名称</label><br><br>
{{ form_obj.name }} # 自动生成表单,同时可以保留上次输入的内容
<span>{{ form_obj.errors.name.0 }}</span> # 返回的错误信息
</div>
</form>
widgets (插件)
常用的插件:
示例:
- Form 代码:
class TestForm(forms.Form):
input_fied = forms.CharField(
widgets.TextInput
)
input_pass_field = forms.CharField(
widgets.PasswordInput
)
textarea_field = fields.CharField(
widgets.Textarea
)
# 单选下拉框
select_field = forms.CharField(
# 单选的可以用 CharFiled
widget=widgets.Select(choices=[(1,"上海"),(2,"北京"),(3,"广州"),])
)
# 多选下拉框
# 多选必须用 ChoiceFiled,因为选择的值是个列表
choices_field = forms.ChoiceField(
# 在多选下拉框中,forms.ChoiceField 就有 choices 字段,
# 所有不会再用 widgets.SelectMultiple的字段了
choices=[(1, "上海"), (2, "北京"), (3, "广州")],
widget=widgets.SelectMultiple()
)
radios_field = forms.IntegerField(
widget=widgets.RadioSelect(choices=[(1,'上海'),(2,'北京'),])
)
# 单个选择框,打对勾的那种
checkbox_field = forms.CharField(
widget=widgets.CheckboxInput()
)
# 多个选择框
check_field = forms.ChoiceField(
choices=[(1, '上海'), (2, '北京'), ],
widget=widgets.CheckboxSelectMultiple()
)
# 文件对象
file_field = forms.FileField()
- 视图函数代码:
def test(request):
if request.method == "GET":
form_obj = TestForm()
return render(request,'test.html',{'form_obj':form_obj})
else:
# obj = TestForm(request.POST,files=request.FILES)
# if obj.is_valid():
# file_obj = obj.cleaned_data['n8']
# file_obj.name # 上传的文件名称
# f = open(file_obj.name,'wb')
# for chunk in file_obj.chunks():
# f.write(chunk)
# f.close()
# else:
# pass
# import os
# file_obj = request.FILES.get('n8')
# print(type(file_obj))
# # file_obj.size
# f = open(file_obj.name, 'wb')
# for chunk in file_obj.chunks():
# f.write(chunk)
# f.close()
return redirect('/test')
给表单添加默认值
def test(request):
if request.method == "GET":
form_obj = TestForm(
# initial 表明了需要添加字段的默认值,
initial={'select_field': 2,'choices_field ': [1,3],'radios_field':2}
)
return render(request,'test.html',{'form_obj':form_obj})
·