Django框架之form表单模型类
form表单模型类,类似Django模型
创建
from django import forms
class Biao(forms.Form):
user = forms.CharField()
ail = forms.EmailField()
映射关系
属性名>>>input提交的name
属性的值>>>input的type类型
可以将实例替换进模板中
{{ biao }} # 实例对象替换进模板中 <label for="id_user">User:</label><input type="text" name="user" required="" id="id_user"> <label for="id_ail">Ail:</label><input type="email" name="ail" required="" id="id_ail"> {{ biao.as_p }} # 调用方法 <form action="" method="post"> <label for="id_user">User:</label><input type="text" name="user" required="" id="id_user"> <label for="id_ail">Ail:</label><input type="email" name="ail" required="" id="id_ail"> {{ biao.as_ul }} <li><label for="id_user">User:</label> <input type="text" name="user" required="" id="id_user"></li> <li><label for="id_ail">Ail:</label> <input type="email" name="ail" required="" id="id_ail"></li>
输出中没有form,ul和submit需要手动添加.
还可以实例.属性,是一个单独的input框,一般情况下都是用这种方式
{{ biao.user }} {{ biao.ail }} <input type="text" name="user" required="" id="id_user"> <input type="email" name="ail" required="" id="id_ail">
表单数据动态渲染
有时我们要将数据库中的数据作为表单的选项,但是如果将该查询结果直接赋值,不能动态检测数据库变化,这里提供了两种方式
方式一,重写构造方法
from django.forms import Form from django.forms import fields class UserForm(Form): name = fields.CharField(label='用户名',max_length=32) email = fields.EmailField(label='邮箱') ut_id = fields.ChoiceField( # choices=[(1,'二笔用户'),(2,'闷骚')] choices=[] ) def __init__(self,*args,**kwargs): super(UserForm,self).__init__(*args,**kwargs) self.fields['ut_id'].choices = models.UserType.objects.all().values_list('id','title')
方式二,使用ModelChoiceField字段,渲染结果依赖于model的__str__方法
from django.forms import Form from django.forms import fields from django.forms.models import ModelChoiceField class UserForm(Form): name = fields.CharField(label='用户名',max_length=32) email = fields.EmailField(label='邮箱') ut_id = ModelChoiceField(queryset=models.UserType.objects.all())
form验证数据
创建一个 Form 对象,传入一个数据字典,把字段名映射到数据上:
from django import forms class ContactForm(forms.Form): subject = forms.CharField() email = forms.EmailField(required=False) message = forms.CharField() # 也可将request.POST传入,他是一个类似字典的对象 f = ContactForm({'subject': 'Hello', 'email': 'adrian@example.com', 'message': 'Nicesite!'}) # 为 Form 实例关联数据后,得到了一个“受约束的”表单: >>> f.is_bound True # 然后,在受约束的表单对象上调用 is_valid() 方法,检查数据是否符合条件。前面为各个字段传入的值都是有 效的,因此这个表单对象是完全有效的: >>> f.is_valid() True
查看错误消息
每个受约束的表单实例都有一个 errors 属性,它的值是一个字典,把字段名称映射到错误消息列表上:
>>> f = ContactForm({'subject': 'Hello', 'message': ''}) >>> f.errors {'message': ['This field is required.']}
因此,我们也可以将错误信息替换入模板中
{{ f.errors.message.0 }} # 因为或许可能有多条错误信息,所以我们只显示第一条
获取全局钩子的错误信息
# 后端获取 实例.errors.get('__all__') # 前端获取 {{ 实例.non_field_errors.0 }}
Django自带规则和定制错误信息
只需要在类属性中传入参数就可以了
from django import forms class LoginFrom(forms.Form): user=forms.CharField(max_length=12,min_length=5, error_messages={ "required":"不能为空", "min_length":"最小长度为5" } ) # error_messages中存放着产生错误的条件和错误提示
input标签的样式设置
# 在类属性中传入widget关键字参数 from django.forms import widgets class LoginFrom(forms.Form): pwd=forms.CharField( error_messages={ "required": "不能为空", "invalid":"格式错误" }, widget=widgets.PasswordInput(attrs={"class":"active abc","egon":"9000"}) ) # PasswordInput插件是password,另外还有很多的内置插件 # attrs={}就是标签里的属性
自定义规则
模型类剖析
class BaseForm(object):forms.Form继承了该类 def is_valid(self): """ self.errors返回的错误信息,如果有不符合条件的信息错误信息中有数据not self.errors为False, 就返回False """ return self.is_bound and not self.errors # errors是是一个方法 @property def errors(self): "Returns an ErrorDict for the data provided for the form" # self._errors 在上面定义为None if self._errors is None: self.full_clean() return self._errors # self.full_clean() 向self._errors中填充数据 def full_clean(self): self._errors = ErrorDict() if not self.is_bound: # 如果字段没有传入值就直接返回,self._errors中并没有数据 return self.cleaned_data = {} # 如果表单被允许为空,那么任何表单数据都没有 if self.empty_permitted and not self.has_changed(): return self._clean_fields() self._clean_form() self._post_clean() # self._clean_fields() 函数 def _clean_fields(self): for name, field in self.fields.items(): # name 是类的属性名 if field.disabled: value = self.get_initial_for_field(field, name) else: value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name)) try: if isinstance(field, FileField): initial = self.get_initial_for_field(field, name) value = field.clean(value, initial) # field.clean()用于校验,如果不满足条件则会抛ValidationError异常 else: value = field.clean(value) self.cleaned_data[name] = value # 如果上面执行完了,说明他是满足条件的 # 下面就是我们自定义规则的基础了,django会去查找clean_+ 类属性名的名字并且执行他 # 所以我们只要在类里定义一个符合规则的用于校验的函数,符合返回value,不符合也抛出ValidationError即可 # 但是这里要注意,cleaned_data[name]是在每一次循环时加入的,所以在clean_name函数中在cleaned_data中取值时,一定要注意,值可能会不存在 if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() # 当clean_name产生异常时,name既存在于cleaned_data中,又存在于add_error中 self.cleaned_data[name] = value except ValidationError as e: self.add_error(name, e) # self._clean_form() 跟在self._clean_fields() 之后,所以当self._clean_form()执行时前面的是执行完毕的 def _clean_form(self): try: cleaned_data = self.clean() # 这个函数内部执行了self.clean(),下面看self.clean() except ValidationError as e: self.add_error(None, e) else: if cleaned_data is not None: self.cleaned_data = cleaned_data def clean(self): # 其内部并没有实现任何逻辑,只是返回了self.cleaned_data,所以我们可以在他的子类中重写该方法,来做全局校验 return self.cleaned_data
自定义实例
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from django import forms class LoginFrom(forms.Form): user=forms.CharField(max_length=12,min_length=5, error_messages={ "required":"不能为空", "min_length":"最小长度为5" } ) pwd=forms.CharField( error_messages={ "required": "不能为空", "invalid":"格式错误" }, widget=widgets.PasswordInput(attrs={"class":"active abc","egon":"9000"}) ) # 校验user字段:自定义规则 # 局子钩子 def clean_user(self): val=self.cleaned_data.get("user") if not val.isdigit(): return val else: raise ValidationError("用户名不能全为数字!") # 全局钩子 def clean(self): user=self.cleaned_data.get("user") pwd=self.cleaned_data.get("pwd") if user==pwd: return self.cleaned_data else: raise ValidationError("两次密码不一致!")
更多字段及参数
Field required=True, 是否允许为空 widget=None, HTML插件 label=None, 用于生成Label标签或显示内容 initial=None, 初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) 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类型 ...
更多内置插件
TextInput(Input) NumberInput(TextInput) EmailInput(TextInput) URLInput(TextInput) PasswordInput(TextInput) HiddenInput(TextInput) Textarea(Widget) DateInput(DateTimeBaseInput) DateTimeInput(DateTimeBaseInput) TimeInput(DateTimeBaseInput) CheckboxInput Select NullBooleanSelect SelectMultiple RadioSelect CheckboxSelectMultiple FileInput ClearableFileInput MultipleHiddenInput SplitDateTimeWidget SplitHiddenDateTimeWidget SelectDateWidget