Django框架 Form组件2(生成标签,验证数据,其他)
程序员文章站
2022-03-07 19:45:01
...
一.生成HTML标签
标签是通过插件生成的
1.通过.<field_name>生成:
#在后端:
<obj>=<form_class>(**<acq_data>):生成<input>
#参数说明:
form_class:Form类的名称
acq_data:默认值构成的dict
#也可以在定义Form类时给某个字段单独设置默认值
obj:返回自定义的Form类的实例
#其中封装了用户提交的数据/错误信息...
#在前端:
{{<obj>.<field_name>}}:生成指定字段的<input>
{{<obj>.<field_name>.<attr>}}:显示指定字段的指定参数的值
#得到Field类的某个子类的实例对象,但显示在页面上的是<input>
#参数说明:
field_name:字段名称
attr:字段的属性(可取的值参见 Form组件1.二.2 部分)
2.通过.as_< type>生成:
#在后端:同(1)
#在前端:
{{<obj>.as_<type>}}:生成obj中所有字段构成的指定类型的标签
#不推荐(定制样式时难以定位)
#参数说明:
type:标签类型;可选值见下
#type的可选值:1个字段对应1个该类型的标签
#注意:标签内仍有<input>用于输入,还有其他内容(如label参数)所在的标签
#相当于将1个字段的输入框及所有相关内容包进1个标签
{{<obj>.as_p}}:生成<p>
{{<obj>.as_ul}}:生成<li>(注意外面包上<ul>)
{{<obj>.as_table}}:生成<tr>(注意外面包上<table>)
...
二.验证用户输入的数据:
1.内置方法:
<obj>=<form_class>(**<data>):生成实例
<re>=<obj>.is_valid():进行验证
<obj>.cleaned_data:由验证成功的数据构成的dict
#参数说明:
form_class:自定义的Form类的名称
data:用户提交的数据构成的dict
obj:自定义的Form类的实例
re:返回验证结果(是否验证成功);为bool
<obj>.errors:全部的错误信息
<obj>.errors.<field_name>:指定字段的错误信息
#参数说明:
obj:自定义的Form类的实例
field_name:字段名称
#转换错误信息的数据类型:
<obj>.errors默认的数据类型是ErrorDict
格式:{["__all__":[<error1>,<error2>...],]#整体的错误信息,来自clean()
"<field_name1>":[<error1>,<error2>...],
"<field_name1>":[<error1>,<error2>...],
...
}
#以下各方法将其转换成其他数据类型:
<obj>.errors.as_ul():默认请求<obj>.errors时会调用该方法
<obj>.errors.as_json()
<obj>.errors.as_data()
<obj>.errors.as_text()
2.自定义验证规则
前2种基于正则表达式的扩展,第3种基于源码的验证流程
(1)通过validators参数:
from django.forms import Form,widgets,fields
from django.core.validators import RegexValidator
class MyForm(Form):
user=fields.CharField(
validators=[
RegexValidator(r'^[0-9]+$','请输入数字'),
RegexValidator(r'^159[0-9]+$','数字必须以1/5/9开头')
#第1个参数为用于验证的正则表达式,第2个参数为错误提示
],
)
import re
from django.forms import Form,widgets,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):
phone=fields.CharField(
#使用自定义验证规则:
validators=[mobile_validate,],
error_messages={'required':'手机不能为空'},
widget=widgets.TextInput(
attrs={
'class':"form-control",
'placeholder':u'手机号码'
}
)
)
(2)通过RegexField:
from django.forms import Form,widgets,fields
from django.core.validators import RegexValidator
class MyForm(Form):
user=fields.RegexField(r'^[0-9]+$',error_message={"invalid":"用户名格式错误"})
(3)通过钩子函数:
对每个字段来说,在进行完基于正则表达式的验证后
将进行基于clean_<field_name>()(1个钩子函数)的验证
#在1个方法中务必只对1个字段进行验证(因为验证时要对所有字段进行循环,不能确定验证的顺序)
#相关源码(见上图)属于is_valid()中的self.errors中的self.full_clean()中的self._clean_fields()
#参数说明:
field_name:该字段的字段名
#实例:
from django.forms import fields,widgets,Form
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class FInfo(Form):
username=fields.CharField(
max_length=5,
validators=[
RegexValidator(r'^[0-9]+$','Enter a valid extension.','invalid')
],
)
email=fields.EmailField()
def clean_username(self):
"""
Form中字段中定义的格式匹配完之后,执行此方法进行验证
clean_username()的规则为:username中不得包含'666'
"""
value=self.cleaned_data['username']
if "666" in value:
raise ValidationError('666已经被玩烂了...','invalid')
return value#如果没有设置返回值,该字段在cleaned_data中的值将是None
##########################################################
在full_clean()中,执行完self._clean_fields()会执行self._clean_forms()
该函数中会执行self.clean()()(另1个钩子函数),钩子本身没有内容,留给用户自定制
#相关源码(见下图)属于is_valid()中的self._clean_forms()
#该方法用于同时对多个字段进行验证
#实例:
from django.forms import fields,widgets,Form
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class AjaxForm(Form):
username=fields.CharField()
user_id=fields.IntegerField(
widget=widgets.Select(choices=[(0,"a"),(1,"b"),(2,"c")])
)
def clean(self):
value_dict=self.cleaned_data
u1=value_dict.get("username")
u2=value_dict.get("user_id")
if u1=="root" and u2==0:
raise ValidationError("整体错误信息")
#这条错误信息会给"__all__"字段
return self.cleaned_data
#同样必须返回self.cleaned_data
在full_clean()中,执行完self._clean_forms()会执行self._post_clean()(另1个钩子函数)
也可以在该函数中自定制验证规则(甚至不需要返回值),但不常用(该函数的源码见下图)
(4)同时生成多个标签进行验证:
from django.forms import Form,widgets,fields
from django.core.validators import RegexValidator
########################自定义字段#########################
class PhoneField(fields.MultiValueField):
def __init__(self, *args, **kwargs):
#Define one message for all fields:
error_messages={
'incomplete':'Enter a country calling code and a phone number'
}
#Or define a different message for each field:
f=(
fields.CharField(
error_messages={
'incomplete':'Enter a country calling code'
},
validators=[
RegexValidator(
r'^[0-9]+$',
'Enter a valid country calling code.'
),
],
),
fields.CharField(
error_messages={
'incomplete':'Enter a phone number.'
},
validators=[
RegexValidator(
r'^[0-9]+$',
'Enter a valid phone number.'
)
],
),
fields.CharField(
validators=[
RegexValidator(
r'^[0-9]+$',
'Enter a valid extension.'
)
],
required=False,
),
)
super(PhoneField,self).__init__(
error_messages=error_messages,
fields=f,
require_all_fields=False,
*args,
**kwargs
)
def compress(self,data_list):
"""
当用户验证都通过后,该值返回给用户
"""
return data_list
########################自定义插件#########################
class SplitPhoneWidget(widgets.MultiWidget):
def __init__(self):
ws=(
widgets.TextInput(),
widgets.TextInput(),
widgets.TextInput(),
)
super(SplitPhoneWidget, self).__init__(ws)
def decompress(self, value):
"""
处理初始值,当初始值initial不是列表时,调用该方法
"""
if value:
return value.split(',')
return [None, None, None]
三.其他
1.ModelForm
在使用Model和Form时,都需要对字段进行定义并指定类型
通过ModelForm则可以省去From中字段的定义
class AdminModelForm(forms.ModelForm):
class Meta:
model=models.Admin
#fields='__all__'
fields=('username','email')
widgets={
'email':forms.PasswordInput(
attrs={'class':"alex"}
),
}
2.实时更新
使用选择标签时,choices的选项可以从数据库中获取,但由于是静态字段
其值无法实时更新,即在数据库中增加记录后需重启项目后才能获取到
#说明:静态字段和类绑定,只有在创建类时才会更新,而刷新页面只会导致重新创建实例对象
因此需要自定义构造方法来实现实时更新
(1)方法1(推荐):
通过__init__()来实现
from django.forms import Form,widgets,fields
from django.core.validators import RegexValidator
class MyForm(Form):
user=fields.ChoiceField(
initial=2,
widget=widgets.Select
)
def __init__(self,*args,**kwargs):#创建对象时会执行
super(MyForm,self).__init__(*args,**kwargs)
self.fields['user'].widget.choices=models.Classes.objects.value_list('id','caption')
#self.fields必须在super()下面:因为super()会把所有静态字段拷贝给self.fields
#如果顺序颠倒,self.fields会找不到静态字段user
(2)方法2:
使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现
#定制化操作不好,会导致强耦合(需要依赖models.py中对应类中的__str__()),不推荐
from django import forms
from django.forms import fields,widgets,models as form_model
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class FInfo(forms.Form):
authors=form_model.ModelMultipleChoiceField(queryset=models.UserInfo.objects.all())
#authors=form_model.ModelChoiceField(queryset=models.UserInfo.objects.all())
#############在mode;s.py中的对应类中补充如下代码#############
def __str__(self):
return self.username#username是要显示的数据库中的字段
#如果不加__str__()见下图1,如果加__str__()见下图2(仅为效果,非本例实际结果)