Form组件的介绍:
我们以前在HTML页面中利用form表单向后端提交数据,都会写一些input标签来,用form表单把它们包起来。
同是对input框做校验,把错误信息放在框下面。这一些Form组件都可以帮你做,因此只有在需要提交数据的时候才需要form组件。
Form的功能:
1.在页面上生成input框等一些HTML标签;
2.对用户输入的数据进行自动的校验;
3.保留上次输入的内容,不会因为一个出现错误,其他的输入全消失。
views.py
# 使用 Django 中form组件来实现注册功能;
from django import forms
from django.forms import widgets
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
# 按照 Django form组件中的要求,自己写一个类;
# strip=True 移除用户输入 前后的空格
class RegForm(forms.Form):
name = forms.CharField(min_length=5, max_length=12, label="账号",strip=True,
widget=widgets.TextInput(attrs={"class":"form-control"}),
error_messages={
"min_length": "最小长度不能小于5",
"max_length": "最大长度不能大于12",
"required": "不能为空"
})
password = forms.CharField(min_length=5, max_length=15, label="密码",
# widget控制的是生成html代码相关的,让密码变成密文了;
# 给Input框添加属性;render_value就是有错误信息的时候,密码不消失
widget=widgets.PasswordInput(attrs={"class":"form-control"}, render_value=True),
error_messages={
"min_length": "最小长度不能小于5",
"max_length": "最大长度不能大于15",
"required": "不能为空"
})
rep_password = forms.CharField(min_length=5, max_length=15, label="确认密码",
# widget控制的是生成html代码相关的,让密码变成密文了;
# 给Input框添加属性;render_value就是有错误信息的时候,密码不消失
widget=widgets.PasswordInput(attrs={"class":"form-control"}, render_value=True),
error_messages={
"min_length": "最小长度不能小于5",
"max_length": "最大长度不能大于15",
"required": "不能为空"
})
phone = forms.CharField(max_length=11, label="手机号",
widget=widgets.TextInput(attrs={"class":"form-control"}),
# 正则化字段的校验RegexValidator(正则,寻找不到后的报错信息)
validators=[RegexValidator(r'^[0-9]+$', "手机号必须是数字"),
RegexValidator(r'^1[3-9][0-9]{9}$',"手机号的格式有误")],
error_messages={
"required": "不能为空",
"invalid" :"格式错误"
})
def login_form(request):
forms_obj = RegForm()
if request.method == "POST":
forms_obj = RegForm(request.POST)
# 用form表单自动的进行校验;如果校验失败的话,会把错误的信息返回给forms_obj;
if forms_obj.is_valid():
# 校验成功后的数据全部都放在 forms_obj.cleaned_data 这一个大字典里面
print(forms_obj.cleaned_data)
# {'name': 'aaaaa', 'password': 'aaaaa', 'rep_password': 'aaaaa'}
del forms_obj.cleaned_data['rep_password']
models.UserInfo.objects.create(**forms_obj.cleaned_data)
return HttpResponse("注册成功!")
return render(request, "login_form.html", {"forms_obj": forms_obj})
模板里面:
<div class="container">
<div class="col-md-5 col-md-offset-3">
<form action="/login_form/" method="post" novalidate>
{% csrf_token %}
<div class="form-group {% if forms_obj.name.errors.0 %}
has-error
{% endif %}">
{# input框的标签名 #}
{{ forms_obj.name.label }}
{# input框 #}
{{ forms_obj.name }}
{# 错误信息是一个列表,取第一个错的信息 #}
<span class="help-block">{{ forms_obj.name.errors.0 }}</span>
</div>
<div class="form-group {% if forms_obj.password.errors.0 %}
has-error
{% endif %}">
{# input框的标签名 #}
{{ forms_obj.password.label }}
{# input框 #}
{{ forms_obj.password }}
{# 错误信息是一个列表,取第一个错的信息 #}
<span class="help-block">{{ forms_obj.password.errors.0 }}</span>
</div>
<div class="form-group {% if forms_obj.rep_password.errors.0 %}
has-error
{% endif %}">
{# input框的标签名 #}
{{ forms_obj.rep_password.label }}
{# input框 #}
{{ forms_obj.rep_password }}
{# 错误信息是一个列表,取第一个错的信息 #}
<span class="help-block">{{ forms_obj.rep_password.errors.0 }}</span>
</div>
<div class="form-group {% if forms_obj.phone.errors.0 %}
has-error
{% endif %}">
{# input框的标签名 #}
{{ forms_obj.phone.label }}
{# input框 #}
{{ forms_obj.phone }}
{# 错误信息是一个列表,取第一个错的信息 #}
<span class="help-block">{{ forms_obj.phone.errors.0 }}</span>
</div>
<div class="form-group {% if forms_obj.city.errors.0 %}
has-error
{% endif %}">
{# input框的标签名 #}
{{ forms_obj.city.label }}
{# input框 #}
{{ forms_obj.city }}
{# 错误信息是一个列表,取第一个错的信息 #}
<span class="help-block">{{ forms_obj.city.errors.0 }}</span>
</div>
<div><input type="submit" value="提交"></div>
{# <hr>#}
{# <div>#}
{# {{ forms_obj.email.label }}#}
{# {{ forms_obj.email }}#}
{# </div>#}
{# <div>#}
{# {{ forms_obj.keep.label }}#}
{# {{ forms_obj.keep }}#}
{# </div>#}
</form>
</div>
</div>
上面的代码也可以循环实现;
使用form组件实现注册功能的步骤:
1.在视图函数中先定义好一个继承forms.Form的类(from django import forms)
2.再在视图函数里面实例化这个form组件的对象;然后把对象给模板去渲染input框等标签;
# city是属于静态的,每次数据库里面更新数据的时候,不能更新到页面上,
#静态的: 获取的值无法实时更新,需要重写构造方法从而实现choice实时更新
# 保存在数据库里面的city为id值;
city = forms.fields.ChoiceField(
# choices=[(1, "篮球"), (2, "足球"), (3, "双色球"), ],
# models.City.objects.all().values_list("id","name")
# <QuerySet [(1, '上海'), (2, '北京'), (4, '哈尔滨'), (3, '武汉')]>
# choices的选项可以配置从数据库中获取
choices= models.City.objects.all().values_list("id","name"), #数据连接到数据库了
label="爱好",
initial=2,
# widget=forms.widgets.Select()
)
#每次更新数据库里面的数据后,不重启服务器的情况下,改写父类的__init__方法,就可以动态的去数据库里面取数据了
# def __init__(self,*args,**kwargs):
# super().__init__(*args,**kwargs)
# # self.fields 为类里面的 所有的对象;self.fields["city"].choices 然后从city对象里面找到choices 属性;
# self.fields["city"].choices = models.City.objects.all().values_list("id","name")
#只要一实例力化这个类,就会执行__init__方法,从数据库里面取值,从而使city静态的属性变成动态了;
# email = forms.EmailField(label="邮箱")
# #单选 radio
# gender = forms.fields.ChoiceField(
# choices=((1, "男"), (2, "女"), (3, "保密")),
# label="性别",
# initial=3,
# widget=forms.widgets.RadioSelect()
# )
#
# #单选的checkbox
# keep = forms.fields.ChoiceField(
# label="是否记住密码",
# initial="checked",
# widget=forms.widgets.CheckboxInput()
# )
#
# #多选的checkbox
# hobby2 = forms.fields.MultipleChoiceField(
# choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
# label="爱好",
# initial=[1, 3],
# widget=forms.widgets.CheckboxSelectMultiple()
# )
#
#
#
# #单选的下拉框
# hobby = forms.fields.ChoiceField(
# choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
# label="爱好",
# initial=3,
# widget=forms.widgets.Select()
# )
# #多选的下拉框
# hobby1 = forms.fields.MultipleChoiceField(
# choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
# label="爱好",
# initial=[1, 3],
# widget=forms.widgets.SelectMultiple()
# )
使用modelform组件实现注册功能的步骤:
1.(from django.forms import ModelForm)也是在视图函数中定义一个继承ModelForm的类,不同的是在这个类下再写一个原类Meta(首字母是大写的);
class StudentList(ModelForm):
class Meta:
model =Student #对应的Model中的类
fields = "__all__" #字段,如果是__all__,就是表示列出所有的字段
exclude = None #排除的字段
#error_messages用法:
error_messages = {
'name':{'required':"用户名不能为空",},
'age':{'required':"年龄不能为空",},
}
#widgets用法,比如把输入用户名的input框给为Textarea
#首先得导入模块
from django.forms import widgets as wid #因为重名,所以起个别名
widgets = {
"name":wid.Textarea(attrs={"class":"c1"}) #还可以自定义属性
}
#labels,自定义在前端显示的名字
labels= {
"name":"用户名"
}
ModelForm是基于Form的组件的,Form的功能,ModelForm都有,只不过ModelForm有额外的功能;
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类型
Form组件的校验也适用于ModelForm:
在Form类中定义钩子函数
局部钩子:
Fom类中定义 clean_字段名() 方法;可以对特定的字段进行校验;返回的是对应字段的值;
# 自定义的校验 is_valid()
# 改写父类的clean+对象的名 的方法的话,就不需要自己把对象的名传入_errors字典里面
def clean_rep_password(self):
value1 = self.cleaned_data["password"]
value2 = self.cleaned_data["rep_password"]
if value1 != value2:
raise ValidationError("两次密码不一致")
return value1
def clean_name(self):
l = ["***","苍井空","...."]
value = self.cleaned_data["name"]
# filter 在数据库里面找不到的话,返回一个[]
ret_value = models.UserInfo.objects.filter(name=value)
if ret_value:
raise ValidationError("用户名已被注册")
for i in l:
if i in value:
raise ValidationError("用户名有敏感词")
return value
全局的钩子是clean方法:就能够实现对字段进行全局校验。
返回的是self.cleaned_data;
#如果改写父类的clean方法的话,因为报错的时候,源码没有传字段名,因此必须自己把对象的名填进错误的字典里面;
# def clean(self):
# value1 = self.cleaned_data["password"]
# value2 = self.cleaned_data["rep_password"]
# if value1 != value2:
# #源码没有传字段名,因此必须自己把字段名填进错误的字典里面;
# self.add_error("rep_password",ValidationError("两次密码不一致"))
# raise ValidationError("两次密码不一致")
# return self.cleaned_data
ModelForm的特别之处在于将数据保存到数据库的方便。
from django.forms import ModelForm class Modelform_based(ModelForm): class Meta: model = self.model fields = "__all__" form = Modelform_based() for boundfield in form: # print(type(boundfield.field)) #<class 'django.forms.boundfield.BoundField'> # print(boundfield.name,type(boundfield.name)) # authors <class 'str'> from django.forms.models import ModelChoiceField from django.forms.boundfield import BoundField if isinstance(boundfield.field, ModelChoiceField): boundfield.is_pop = True # boundfield.field.queryset.model 字段对象.queryset 拿到对于字段对象(authors)的模型表对象(author); print(boundfield.field.queryset.model) app_name = boundfield.field.queryset.model._meta.app_label model_name = boundfield.field.queryset.model._meta.model_name
if request.method == "POST": form = Modelform_based(request.POST) if form.is_valid(): add_obj = form.save() # 添加的数据有返回值 就是 返回当前添加的数据。 #按照对应的字段 添加值
if request.method == "POST": form = Modelform_based(request.POST, instance=edit_obj) #编辑 if form.is_valid(): form.save()
model表的字段 转变为 modelform的字段:
class Book(models.Model):
title=models.CharField(max_length=32)
price=models.DecimalField(max_digits=8,decimal_places=2) # 999999.99
date=models.DateField()
publish=models.ForeignKey("Publish")
authors=models.ManyToManyField("Author")
class BookForm(forms.Form):
title = forms.CharField(max_length=32,label="书籍名称")
price = forms.DecimalField(max_digits=8, decimal_places=2,label="价格") # 999999.99
date = forms.DateField(label="日期",
widget=widgets.TextInput(attrs={"type":"date"})
)
#gender=forms.ChoiceField(choices=((1,"男"),(2,"女"),(3,"其他")))
#publish=forms.ChoiceField(choices=Publish.objects.all().values_list("pk","title"))
publish=forms.ModelChoiceField(queryset=Publish.objects.all())
authors=forms.ModelMultipleChoiceField(queryset=Author.objects.all())
forms.ChoiceField(继承Field) ---- select 元组套元组 forms.ModelChoiceField(ChoiceField) ----select(单选) forms.ModelMultipleChoiceField(ModelChoiceField) ----select multiple(多选)
详细(点我)