一、 form介绍
我们之前在前端页面中利用form表单,是为了向后端提交数据,在form表单中我们写一些input标签,然后用form表单把他们包裹起来,再给它提交路劲就可以帮我们提交数据了,但是以前这种方法比较繁琐,麻烦而且我们要对用户名和密码进行验证时还要写多个正则表达式来进行匹配。可想而知,代码量是非常的大,而且重复代码也很多。所以今天介绍一个在Django中很厉害的一个组件-------form组件
我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息,Django form组件就实现了上面所述的功能。
Django的Form主要具有一下几大功能:
- 生成HTML标签
- 验证用户数据(显示错误信息)
- HTML Form提交保留上次提交数据
- 初始化页面显示内容
普通方式写的注册功能
view.py
# 注册
def login(request):
dic={'username':'root','pwd':'123456'}
error_msg=''
if request.method=="POST":
username=request.POST.get('username')
pwd=request.POST.get('pwd')
print(username,pwd)
if dic['username']!=username or dic['pwd']!=pwd:
error_msg='用户名或密码错误'
else:
return HttpResponse('注册成功!')
return render(request,'login.html',{'error_msg':error_msg})
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<form action="/login/" method="POST">
{% csrf_token %}
<p>
用户名:
<input type="text" name="username">
</p>
<p>
密码:
<input type="password" name="pwd">
</p>
<p>
用户名:
<input type="submit" value="注册"><p style="color: red">{{ error_msg }}</p>
</p>
</form>
</body>
</html>
二、 小试牛刀
1、创建Form类
from django.forms import Form
from django.forms import fields
from django.forms import widgets
from django import forms
class Myform(Form):
username=fields.CharField(
min_length=6,
max_length=12,
label='用户名',
widget=widgets.TextInput(),
error_messages={'required': '不能为空', 'invalid': '格式错误'}
)
pwd=fields.CharField(
min_length=6,
max_length=8,
label='密码',
widget=widgets.PasswordInput(),
error_messages={'required': '不能为空', 'invalid': '格式错误'}
)
email=fields.EmailField(
error_messages={'required': '不能为空', 'invalid': '格式错误'}
)
gender=fields.CharField(
label='性别',
initial=2,
widget=widgets.RadioSelect(choices=((1, '男'), (2, '女'))),
)
city=fields.CharField(
label='城市',
initial=2,
widget=widgets.Select(choices=((1, '上海'), (2, '北京'),)),
)
2、View函数处理
from django.shortcuts import render,redirect,HttpResponse
def new_login(request):
if request.method=="GET":
obj=Myform()
return render(request,'new_login.html',{'obj':obj})
elif request.method=='POST':
obj=Myform(request.POST)
if obj.is_valid():
value=obj.cleaned_data
print(value)
else:
error=obj.errors
print(error)
return render(request,'new_login.html',{'obj':obj})
3、生成HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/new_login/" method="POST">
#{{ obj.as_p }} 直接写上这个下面的都不用写,除此之外还有{{ obj.as_table }},{{ obj.as_ul }},可以分别试一试看看其效果
{% csrf_token %}
<p>
<label for="{{ obj.username.id_for_label }}">
{{ obj.username.label }}
</label>
{{ obj.username }}{{ obj.username.errors }}
</p>
<p>
<label for="{{ obj.pwd.id_for_label }}">
{{ obj.pwd.label}}
</label>
{{ obj.pwd }}{{ obj.pwd.errors }}
</p>
<p>
<label for="{{ obj.email.id_for_label }}">
{{ obj.email.label }}
</label>
{{ obj.email }}{{ obj.email.errors }}
</p>
<p>
<label for="{{ obj.gender.id_for_label }}">
{{ obj.gender.label }}
</label>
{{ obj.gender }}{{ obj.gender.errors }}
</p>
<p>
<label for="{{ obj.city.id_for_label }}">
{{ obj.city.label}}
</label>
{{ obj.city }}{{ obj.city.errors }}
</p>
<input type="submit" value="注册">
</form>
</body>
</html>
网页效果也验证了form的功能:
前端页面是form类的对象生成的–>生成HTML标签功能
当用户名和密码输入为空或输错之后 页面都会提示–>用户提交校验功能
当用户输错之后 再次输入 上次的内容还保留在input框–>保留上次内容
三、 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=[], 自定义正则
disabled=False, 是否可以编辑
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, 小数位长度
BaseTemporalField()
input_formats=None 时间格式化
DateField() 格式:2050-02-08
TimeField() 格式:13:14
DateTimeField()格式:2050-02-08 13:14
DurationField() 时间间隔:%d %H:%M:%S.%f
...
RegexField()
regex, 自定制正则表达式
max_length=None, 同上
min_length=None, 同上
error_message=None, 同上
EmailField()
...
FileField()
allow_empty_file=False 是否允许空文件
ImageField()
...
注:需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
- form表单中 enctype="multipart/form-data"
- view函数中 obj = MyForm(request.POST, request.FILES)
URLField()
...
ChoiceField()
...
choices=(), 选项,如:choices = ((0,'天津'),(1,'深圳'),)
required=True, 是否必填
widget=None, 插件,默认select插件
label=None, Label内容
initial=None, 初始值
help_text='', 帮助提示
ModelChoiceField()
...
queryset, # 查询数据库中的数据
empty_label="---------", # 默认空显示内容
to_field_name=None, # HTML中value的值对应的字段
limit_choices_to=None # ModelForm中对queryset二次筛选
ModelMultipleChoiceField()
...
TypedChoiceField()
coerce = lambda val: val 对选中的值进行一次转换
empty_value= '' 空值的默认值
MultipleChoiceField()
...
TypedMultipleChoiceField()
coerce = lambda val: val 对选中的每一个值进行一次转换
empty_value= '' 空值的默认值
ComboField(Field)
fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式
MultiValueField()
PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
SplitDateTimeField()
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() 将目录里面的文件显示在前端页面中
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都支持
unpack_ipv4=False
...
FilePathField示例
class Myform(Form):
file=fields.FilePathField(
path='文件夹',
match = None,
recursive = True,
allow_files = True,
allow_folders = False,
)
2、Django常用的选择插件:
radioSelect
radio标签
class Myform(Form):
gender=fields.CharField(
label='性别:',
initial=2,
widget=widgets.RadioSelect(choices=((1, '男'), (2, '女'))),
)
或者
gender=fields.ChoiceField(
choices=((1, '男'), (2, '女')),
label='性别:',
initial=2,
widget=widgets.RadioSelect(),
)
两者页面效果相同,但是要注意区分两者的区别
单选Select
class Myform(Form):
city=fields.CharField(
label='城市:',
initial=2,
widget=widgets.Select(choices=((1, '上海'), (2, '北京'),)),
)
多选Select
class Myform(Form):
city = fields.MultipleChoiceField(
label='城市:',
initial=[1,2],
widget=widgets.SelectMultiple(choices=((1, '上海'), (2, '北京'),)),
)
单选checkbox
class Myform(Form):
keep=fields.ChoiceField(
label='是否记住密码:',
initial='checked',
widget=widgets.CheckboxInput()
)
单选checkbox示例
class Myform(Form):
keep=fields.ChoiceField(
label='七天免登录:',
choices=((1,'True'),(0,'False')),
initial=1,
widget=widgets.CheckboxInput()
)
if True:
session 超时时间=7
else:
pass
多选checkbox
class Myform(Form):
hobby=fields.MultipleChoiceField(
choices=((1,'看书'),(2,'玩游戏'),(3,'篮球'),(4,'足球')),
initial=[1,3],
widget=widgets.CheckboxSelectMultiple(),
label='爱好'
)
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 获取的值无法实时更新,需要重写构造方法从而实现choice实时更新。
方式一:
from django.forms import Form
from django.forms import widgets
from django.forms import fields
class MyForm(Form):
user = fields.ChoiceField(
#choices=((1, '天津'), (2, '深圳'),),
initial=2,
widget=widgets.Select()
)
def __init__(self, *args, **kwargs):
#注意重写init方法的时候,*args和**kwargs一定要给人家写上,不然会出问题,并且验证总是不能通过,还不显示报错信息
super(MyForm,self).__init__(*args, **kwargs)
# self.fields['city'].widget.choices = ((1, '上海'), (2, '北京'),)
# 或
self.fields['city'].widget.choices = models.City.objects.all().value_list('id','didian')
方式二:
from django import forms
from django.forms import fields
from django.forms import models as form_model
class MyFrom(forms.Form):
authors = form_model.ModelMultipleChoiceField(queryset=models.City.objects.all())
# authors = form_model.ModelChoiceField(queryset=models.City.objects.all())
四、 自定义验证规则
方式一:
from django.core.validators import RegexValidator
class Myform(Form):
phone=fields.CharField(
validators=[ RegexValidator('^139[0-9]+$', '数字必须以139开头')]
)
方式二:
from django.core.validators import RegexValidator
import re
from django.forms import ValidationError
from django import forms
def re_ce(value):
m=re.compile('^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not m.findall(value):
raise ValidationError('手机号格式错误')
class Myform(forms.Form):
phone=fields.CharField(
validators=[ re_ce]
)
五、 Hook钩子方法
除了上面两种方式,我们还可以在Form类中定义钩子函数,来实现自定义的验证功能。
定义局部钩子
我们在Fom类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验。
class Myform(Form):
username=fields.CharField(
initial='root',
min_length=6,
max_length=12,
label='用户名:',
widget=widgets.TextInput(),
error_messages={'required': '不能为空', 'invalid': '格式错误'}
)
def clean_username(self):
value = self.cleaned_data.get("username")
if "root" not in value:
raise ValidationError("用户名错误")
else:
return value
定义全局钩子
我们在Fom类中定义 clean() 方法,就能够实现对字段进行全局校验,字段全部验证完,局部钩子也全部执行完之后,执行这个全局钩子校验
class MyForm(forms.Form):
password=fields.CharField(min_length=6,
max_length=12,
label='密码',
error_messages={'required':'密码不能为空','min_length':'密码长度不能小于6','max_length':'密码长度不能大于12'},
widget=widgets.PasswordInput(attrs={'class':'c2'}))
r_password = fields.CharField(min_length=6,
max_length=12,
label='确认密码',
error_messages={'required': '密码不能为空', 'min_length': '密码长度不能小于6',
'max_length': '密码长度不能大于12'},
widget=widgets.PasswordInput(attrs={'class': 'c2','name':'re_password')
def clean(self):
p1=self.cleaned_data['password']
p2 = self.cleaned_data['r_password']
if p1==p2:
return self.cleaned_data
else:
self.add_error('r_password','两次密码不一致')
raise ValidationError('两次密码不一致')