1. 常规表单
2. django中的form表单
form表单使用介绍
页面与web服务器交互数据最重要的方式;
input
标签类型:text
,password
,file
;默认方式
method=get
,所有信息都会在url后面显示,表单提交指定为method=post
;注意,若需要将数据发送到后端,
input
标签必须指定name
属性,否则不会发送;传输的数据以键值对形式保存在
request.POST
中, 键的值即为name
属性值;- 文件上传: 由于上传文件时在客户端与服务端传输的是二进制数据,与字符串数据不一样。传输二进制数据,不管是在form表单,还是在Ajax中,都有自己的传输方式。
在form表单中,上传文件时要使用分片传输的方式enctype="multipart/form-data"
。若没有指定
form
的传输方式:file
数据保存在request.POST
中, 一般是文件名的str
形式;若指定了文件传输类型:
enctype="multipart/form-data"
,表单提交后文件以InMemoryUploadedFile
的类对象单独保存在request.FILES
中,
保存文件: 后台接收的文件对象无法直接通过
open
方法读取, 直接遍历文件对象即可;// 接受文件对象并保存至服务器 file_obj = request.FILES.get('upload_file') f1 = open(file_obj.name, "wb") # 方法1 for i in file_obj: f1.write(i) # 方法2 for i in file_obj.chunks(): f1.write(i)
Django的form表单介绍
form表单的升级版, 可以给表单元素扩展许多功能:
- 在模板生成html的input标签;
- 验证用户数据并显示错误信息;
- 保留上次提交数据;
- 初始化页面显示内容;
form字段及参数
required=True, 是否允许为空
widget=None, HTML插件
disabled=False, 是否可以编辑
initial=None, 初始值
label=None, 用于生成Label标签或显示内容
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}, 此处和model字段定义错误信息类似, 也有max_length, min_length等错误信息code
show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[], 自定义验证规则
localize=False, 是否支持本地化
label_suffix=None Label内容后缀
CharField
max_length=None, 最大长度
min_length=None, 最小长度
strip=True 是否移除用户输入空白
IntegerField
max_value=None, 最大值
min_value=None, 最小值
FloatField
...
DecimalField
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 总长度
decimal_places=None, 小数位长度
RegexField
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略错误信息,错误信息使用 error_messages={'invalid': '...'}
EmailField
...
# 重要
FileField
allow_empty_file=False 是否允许空文件
ImageField
需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
1. form表单中 enctype="multipart/form-data"
2. view函数中 obj = MyForm(request.POST, request.FILES)
URLField
...
BooleanField
...
ChoiceField
choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),)
required=True, 是否必填
widget=None, 插件,默认select插件
label=None, Label内容
initial=None, 初始值
help_text='', 帮助提示
FilePathField 文件选项,目录下文件显示在页面中
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
required=True,
widget=None,
label=None,
initial=None,
help_text=''
常用控件
// radio单选按钮,值为字符串==========================
gender = forms.ChoiceField(
choices=((1, '男性'),(2, '女性'), (3, '中性'), ), initial=1,
widget=widgets.Radioselect
)
gender = forms.CharField(
initial=1, widget=widgets.RadioSelect(choices=((1, '男性'),(2, '女性'), (3, '中性'), ))
)
// 单select,值为字符串========================================
user = fields.CharField(
initial=2,
widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
)
user = fields.ChoiceField(
choices=((1, '上海'), (2, '北京'),),
initial=2,
widget=widgets.Select
)
// 多选select,值为列表==========================================
user = fields.MultipleChoiceField(
choices=((1,'上海'),(2,'北京'),),
initial=[1,],
widget=widgets.SelectMultiple
)
// 单checkbox
gender = forms.ChoiceField(initial=[2, ],choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxInput)
// 多选checkbox,值为列表
user = fields.MultipleChoiceField(
initial=[2, ],
choices=((1, '上海'), (2, '北京'),),
widget=widgets.CheckboxSelectMultiple
)
关于使用select
等控件时的取值问题
- 先将内容以元祖嵌套格式取出,
Obj.objects.all().values_list(‘id‘, ‘name‘)
; - 作为参数传入;
class UserForm(forms.Form):
choices = City.objects.all().values_list('id', 'name')
hometown = forms.ChoiceField(initial=2, widget=widgets.Select(choices=choices))
Django的form使用步骤
创建后台表单验证类,继承
form.Form
;from django import forms from django.forms import widgets class UserForm(forms.Form): username = forms.CharField(min_length=2, max_length=12, label='用户名', error_messages={'required': '用户名不能为空', 'min_length': '必须大于2位'}, widget=widgets.TextInput(attrs={"placeholder": "用户名", "class": "form-control s", 'label':'username', })) # 可以在后端定制input样式,直接给即将生成的input标签增加属性 password = forms.CharField(label='密码',min_length=4,widget=widgets.PasswordInput(attrs={"placeholder": "密码","class": "form-control"})) email = forms.EmailField(label='邮箱', widget=widgets.PasswordInput(attrs={"placeholder": "密码","class": "form-control"})) gender = forms.ChoiceField(choices=((1, 'male'),(2, 'female')), initial=1, widget=widgets.RadioSelect) hometown = forms.CharField( initial=2, widget=widgets.Select(choices=((1, '武汉'), (2, '广州'),)) )
页面根据类的对象自动创建html标签, 注意, 在form标签中加上
novalidate
可以关闭浏览器的自动验证功能, 方便前期调试;<link rel="stylesheet" href="{% static 'css/bootstrap.css' %}"> <div class="container form"> <div class="row"> <div class="col-md-8"> <form action="" method="post" enctype="multipart/form-data" novalidate> <div class="form-group"> <p>username:{{ form.username }}<span>{{ error_msg.username.0 }}</span></p> </div> <div class="form-group"> <p>password:{{ form.password }}<span>{{ error_msg.password.0 }}</span></p> </div> <div class="form-group"> <p>email:{{ form.email }}<span>{{ error_msg.email.0 }}</span></p> </div> <div class="form-group"> <p>gender:{{ form.gender }}<span>{{ error_msg.gender.0 }}</span></p> </div> <div class="form-group"> <p>hometown:{{ form.hometown }}<span>{{ error_msg.gender.0 }}</span></p> </div> <div class="form-group"><input type="submit"></div> {% csrf_token %} </form> </div> </div> </div>
视图函数接收表单数据, 判断并返回信息
def register(request): error_msg = '' if request.method == 'GET': # 1. 实例化空form对象, 并传到模板,生成空的input标签 my_form = UserForm() return render(request, 'register.html', {"form": my_form, 'error_msg': error_msg}) if request.method == "POST": # 1. 创建form对象,随后利用前端form提交的信息对齐进行填充 form_post = UserForm(request.POST) # 2. 检查提交的表单语法是否正确 if form_post.is_valid(): print(form_post.clean()) # 默认检测所有的输入框不能为空 # 特有的取值方法:通过cleaned_data属性取到所有的输入值 username = form_post.cleaned_data.get('username', '') password = form_post.cleaned_data.get('password', '') email = form_post.cleaned_data.get('email', '') else: # 检查错误信息 # 可以用ie浏览器检测,chrome和火狐都会默认在前端显示错误,不会提交 error_msg = form_post.errors print(error_msg) # <class 'django.forms.utils.ErrorDict'> return HttpResponse('信息有误')
内置错误信息提示
在前面三个步骤的基础上, 对于错误在页面的提示作以下说明,见代码;
注意:
# 简单的错误信息定制,
error_messages={
'required': '用户名不能为空', 'min_length': '必须大于*位',
'invalid' : '****', 'max_length': '不能超过*位',...
},
# is.valid()没通过的分支
error_msg = form_post.errors
print(error_msg) # <class 'django.forms.utils.ErrorDict'>
return render(request, 'register.html', {
"error_msg": error_msg,
'form': form_post,
})
# html页面
<span class="errors">{{ error_msg.username.0 }}</span>
# 有对应的字段错误,就取值并显示
自定制错误信息(重点)
正则匹配的自定义验证
- 存在多条验证时, 以列表或者元祖形式导入实例化的
RegexValidator
对象, django会按照顺序逐个验证,直到抛出错误信息; - 传入两个参数:
匹配的正则表达式
和错误信息
; - 原理是若输入与正则不匹配,则抛出
ValidationError(错误信息)
from django.form import Form
from django.core.validators import RegexValidator
class UserForm(Form):
username = forms.CharField(
validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
widget=widgets.TextInput(attrs={'class': 'form-control'}))
自定义验证函数
from django.core.exceptions import ValidationError
import re
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误')
class UserForm(forms.Form):
username = forms.CharField(validators=[mobile_validate, ], widget=widgets.TextInput(attrs={'class': 'form-control'}),)
重载内置的clean_field方法
- 在form字段中定义的验证完成后,会执行
clean_field()
方法; - 此时通过
cleaned_data
取出要进一步判断的值; - 自定义验证函数必须有返回值, 即合格的值, 不合格直接抛出自定义的错误信息即可;
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
class UserForm(Form):
username = forms.CharField(widget=widgets.TextInput(attrs={'class': 'form-control'}),)
def clean_username(self):
value = self.cleaned_data['username']
if value == 'alex':
raise ValidationError('用户名已存在')
return value
全局自定制验证clean方法
- 实现前提条件: 因为
clean(self)
是在clean_field(self)
之后运行的; - 若验证没用过, 自定制的错误信息会存储在
django.forms.utils.ErrorDict
中,__all__:[‘错误信息‘, ]
; - 在前端不能直接通过
errors_dict.key.0
取出,需要自定义过滤器,取出__all__
的值;
# 例如:验证用户名和邮箱是否一致
// 后台自定制多字段验证方法==============
def clean(self):
if self.cleaned_data.get('username') != self.cleaned_data.get('email'):
raise ValidationError('用户名和邮箱不一致')
else:
return self.cleaned_data
// 自定义过滤器==========================
from django import template
register = template.Library()
@register.filter
def get_error(error_dict):
return error_dict.get('__all__')
// 前端取值=============================
<div id="error_msg">
{% if error_msg %}
{{ error_msg|get_error }}
{% endif %}
</div>