forms组件
1.form介绍
- form组件的主要功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
2.需求案例
- 写一个注册功能
- 获取用户名和密码,利用form表单提交数据
- 在后端判断用户名和密码是否符合一定的条件
- 用户名中不能包含啦啦啦
- 密码不能少于三位
- 如果符合条件需要你将提示信息展示到前端页面
3.需求案例分析
3.1 手动渲染错误信息
- 后端
def register(request):
# 自己定义的字典渲染到前端
error_msg_dict = {}
if request.method == 'POST':
data = request.POST
print(data)
username = data.get("username")
password = data.get("password")
if username != 'serein':
error = f"当前用户名{username}错误"
error_msg_dict["username_error"] = error
if password != '123':
error = f"当前密码{password}错误"
error_msg_dict["password_error"] = error
return render(request, 'register.html', locals())
- 前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<p>
username:<input type="text" name="username">
<span style="color: red">{{ error_msg_dict.username_error }}</span>
</p>
<p>
password:<input type="text" name="password">
<span style="color: red">{{ error_msg_dict.password_error }}</span>
</p>
<p>
<input type="submit">
</p>
</form>
</body>
</html>
3.2 form组件如何校验数据
3.2.1 编写表单类
- 我们可以通过Django提供的Form类来自用生成上面的表单,不再需要手动在HTML中编写
- 在你当前app内新建一个
forms
文件夹,在文件夹下面创建一个forms.py文件 - 书写form表单类
# 导入模块
from django import forms
# 按照Django form组件的要求自己写一个类 MyForm
class UserForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3)
# # username : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3)
# email : 必须符合邮箱格式 [email protected]
email = forms.EmailField()
- 注意事项
- 提前导入forms模块
- 所有的表单类都要继承forms.Form类
- 每个表单字段都有自己的字段类型比如CharField,它们分别对应一种HTML语言中的
<form>
元素中的表单元素。这一点和Django模型系统的设计非常相似。 - 例子中的label用于设置说明标签
max_length
限制最大长度为8。它同时起到两个作用,一是在浏览器页面限制用户输入不可超过8个字符,二是在后端服务器验证用户输入的长度不可超过8。
3.3 基于form组件校验数据
-
后端,需要导入form类
-
from app01.Formcontrol.UserForm import UserForm
def register(request):
error_msg_dict = {}
form_obj = UserForm()
if request.method == 'POST':
data = request.POST
username = data.get("username")
password = data.get("password")
email = data.get("email")
# 生成form对象
form_obj = UserForm({"username": username, "password": password, "email": email})
# 校验数据是否合法,is_valid
form_obj.is_valid()
# 查看合法的数据,cleaned_data
print(f"合法数据{form_obj.cleaned_data}")
# 查看不合法的数据,errors
print(f"不合法数据{form_obj.errors}")
return render(request, 'register.html', locals())
- 前端
<form>
{{ form_obj }}
<p>
<input type="submit">
</p>
</form>
4. FORM组件常用字段参数
4.1 创建Form类时
- 主要涉及到 【字段】 和 【插件】
- 字段用于对用户请求数据的验证
- 插件用于自动生成HTML;
4.2 initial
初始值,input框里面的初始值。
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三" # 设置默认值
)
pwd = forms.CharField(min_length=6, label="密码")
4.3 error_messages
重写错误信息。
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短8位"
}
)
pwd = forms.CharField(min_length=6, label="密码")
4.4 password
class LoginForm(forms.Form):
...
pwd = forms.CharField(
min_length=6,
label="密码",
widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
)
4.5 radioSelect
单选按钮
class LoginForm(forms.Form):
username = forms.CharField(
min_length=8,
label="用户名",
initial="张三",
error_messages={
"required": "不能为空",
"invalid": "格式错误",
"min_length": "用户名最短8位"
}
)
pwd = forms.CharField(min_length=6, label="密码")
gender = forms.fields.ChoiceField(
choices=((1, "男"), (2, "女"), (3, "保密")),
label="性别",
initial=3,
widget=forms.widgets.RadioSelect()
)
4.6 单选Select
class LoginForm(forms.Form):
...
hobby = forms.ChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=3,
widget=forms.widgets.Select()
)
4.7 多选Select
class LoginForm(forms.Form):
...
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
label="爱好",
initial=[1, 3],
widget=forms.widgets.SelectMultiple()
)
4.8 单选框checkbox
class LoginForm(forms.Form):
...
keep = forms.ChoiceField(
label="是否记住密码",
initial="checked",
widget=forms.widgets.CheckboxInput()
)
4.9 多选框checkbox
class LoginForm(forms.Form):
...
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
4.10 required 字段必填
class LoginForm(forms.Form):
...
hobby = forms.MultipleChoiceField(
choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
label="爱好",
required = True
initial=[1, 3],
widget=forms.widgets.CheckboxSelectMultiple()
)
4.11 choice字段注意事项
- 在使用选择标签时
- 需要注意choices的选项可以配置从数据库中获取
- 但是由于是静态字段
- 获取的值无法实时更新
- 需要重写构造方法从而实现choice实时更新。
(1)方式一
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):
super(MyForm,self).__init__(*args, **kwargs)
# self.fields['user'].choices = ((1, '上海'), (2, '北京'),)
# 或
self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')
(2)方式二
from django import forms
from django.forms import fields
from django.forms import models as form_model
class FInfo(forms.Form):
authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # 多选
# authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) # 单选
5. 自定义字段校验
5.1 RegexValidator验证器
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
class MyForm(Form):
user = fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
)
5.2 自定义验证函数
import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError
# 自定义验证规则
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 PublishForm(Form):
title = fields.CharField(max_length=20,
min_length=5,
error_messages={'required': '标题不能为空',
'min_length': '标题最少为5个字符',
'max_length': '标题最多为20个字符'},
widget=widgets.TextInput(attrs={'class': "form-control",
'placeholder': '标题5-20个字符'}))
# 使用自定义验证规则
phone = fields.CharField(validators=[mobile_validate, ],
error_messages={'required': '手机不能为空'},
widget=widgets.TextInput(attrs={'class': "form-control",
'placeholder': u'手机号码'}))
email = fields.EmailField(required=False,
error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
【补充】Django中的form所有内置字段
Field
required=True, 是否允许为空
widget=None, HTML插件
label=None, 用于生成Label标签或显示内容
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}
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类型
6. Form组件渲染HTML代码
- forms组件只会帮我们渲染用户输入的标签(input/select...)
6.1 渲染方式一
(1)路由
# forms 组件渲染html标签
path(r'index/',views.index),
(2)后端代码
from django import forms
# 定义form类
class MyForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3)
# # username : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3)
# email : 必须符合邮箱格式 [email protected]
email = forms.EmailField()
def index(request):
# (1)产生一个空对象
form_obj = MyForm()
# (2) 直接将空对象传递给前端页面
return render(request,'index.html',locals())
(3)前端页面
<form action="" method="post">
<p>第一种渲染方式</p>
{{ form_obj }}
</form>
(4)渲染出的效果
- 页面上会出现对应的HTML代码,但是是一开始没有的
<form action="" method="post">
<p>第一种渲染方式</p>
<label for="id_username">Username:</label><input type="text" name="username" maxlength="8" minlength="3"
required="" id="id_username">
<label for="id_password">Password:</label><input type="text" name="password" maxlength="8" minlength="3"
required="" id="id_password">
<label for="id_email">Email:</label><input type="email" name="email" required="" id="id_email">
</form>
(5)特点
- 代码书写极少,封装程度太高,不便于后期拓展
- 一般情况下用于本地测试使用
6.2 渲染方式二
(1)前端
<form action="" method="post">
<p>第二种渲染方式</p>
{{ form_obj.as_ul }}
</form>
(2)渲染到页面的效果
<form action="" method="post">
<p>第二种渲染方式</p>
<li><label for="id_username">Username:</label> <input type="text" name="username" maxlength="8" minlength="3"
required="" id="id_username"></li>
<li><label for="id_password">Password:</label> <input type="text" name="password" maxlength="8" minlength="3"
required="" id="id_password"></li>
<li><label for="id_email">Email:</label> <input type="email" name="email" required="" id="id_email"></li>
</form>
- 该方法可以修改后端传入的属性值
# 定义form类
class MyForm(forms.Form):
# username : 字符串类型 最小三位,最大八位
username = forms.CharField(max_length=8, min_length=3, label="用户名")
# # username : 字符串类型 最小三位,最大八位 : 字符串类型 最小三位,最大八位
password = forms.CharField(max_length=8, min_length=3)
# email : 必须符合邮箱格式 [email protected]
email = forms.EmailField()
def index(request):
# (1)产生一个空对象
form_obj = MyForm()
# (2) 直接将空对象传递给前端页面
return render(request, 'index.html', locals())
- 前端
<form action="" method="post">
<p>第二种渲染方式</p>
{# 对象点属性取到对应的属性值 #}
{{ form_obj.username.label }} {{ form_obj.username }}
</form>
- 前端页面展示效果
<form action="" method="post">
<p>第二种渲染方式</p>
用户名 <input type="text" name="username" maxlength="8" minlength="3" required="" id="id_username">
</form>
- 特点
- 可扩展性非常强
- 但是需要书写复杂的代码
- 一般情况下不用
6.3 渲染方式三(推荐使用)
- 前端
<form action="" method="post">
<p>推介使用第三种</p>
{% for form in form_obj %}
<p> {{ form.label }} : {{ form }}</p>
{% endfor %}
</form>
- 页面展示
<form action="" method="post">
<p>推介使用第三种</p>
<p> 用户名 : <input type="text" name="username" maxlength="8" minlength="3" required="" id="id_username"></p>
<p> Password : <input type="text" name="password" maxlength="8" minlength="3" required="" id="id_password"></p>
<p> Email : <input type="email" name="email" required="" id="id_email"></p>
</form>
7. 钩子函数
from django import forms
class UserForm(forms.Form):
username = forms.CharField(
max_length=6,
min_length=3,
required=True,
label="用户名",
error_messages={
"max_length": "不能超过 6 位的最大长度",
"min_length": "不能小于 3 位的最小长度"
}
)
password = forms.CharField(
max_length=4,
min_length=2,
label="密码",
)
# 先定义一个函数,函数名开头必须是clean_字段名,局部钩子
def clean_username(self):
print(f"这是在form的局部钩子中")
print(self.cleaned_data) # {'username': 'dd2'}
# 先把需要校验的局部字段勾出来
username = self.cleaned_data.get("username")
# 进行字段的校验
if "666" in username:
self.add_error("username", "当前用户名不能包含 666 !")
# 把校验后的数据扔回去
return username
# 全局钩子
def clean(self):
print(f"全局钩子")
print(self.cleaned_data)
username = self.cleaned_data.get("username")
password = self.cleaned_data.get("password")
# if len(username) == 4:
# self.add_error("username", "用户名不能是4位!")
# if len(password) == 3:
# self.add_error("password", "密码不能是 3 位")
return self.cleaned_data
标签:username,None,form,forms,length,error,组件
From: https://www.cnblogs.com/Formerly/p/18102503