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

  

posted @ 2018-01-31 21:52  瓜田月夜  阅读(418)  评论(0编辑  收藏  举报