django—forms组件

1、forms组件的作用

1、生成页面可用的html标签

2、对用户提交的数据进行校验,并返回校验结果

3、保留上次输入内容

2、如何使用forms组件

2.1 使用前提:定义类

2.1.1 基本结构

# views.py
from django import forms
class MyForm(forms.Form):
    '''
    1、使用类似于orm的表定义,但在此处,一个字段即为一个form表单的标签,简称form类标签
    2、通过字段参数可以控制form类标签的值,如最长位数
    3、通过特殊字段类型,控制特定输入规范,如email字段控制了input输入框必须为邮箱格式,如123@qq.com
    '''
    username = forms.CharField(max_langth=8,min_length=3)
    password = forms.CharField(max_langth=8,min_length=3)
    email = forms.EmailField()

2.1.2 更改标签属性\样式

forms标签默认为input文本输入框,可用参数widget改变标签属性

1、Input前缀相当于前端中input标签的type属性,首字母大写

2、以attrs={key:value}的方式设置标签属性

3、常用的标签类型有:

PasswordInput:密文

CheckboxInput:单选按钮

CheckboxSelectMultiple:多选按钮

RadioSelect:单选框

Select:下拉单选框

SelectMultiple:下拉多选框

class MyForm(forms.Form):
	username = forms.CharField(
    	max_lenget=8,
    	min_length=3,
    	widget=forms.widgets.TextInput(attrs={"class": "form-control"})  # 文本输入框,默认
    	widget=forms.widgets.PasswordInput(attrs={"class": "form-control"})   # 密文输入框       
    )

2.2 校验数据

校验数据,即获取前端的输入,并根据类中定义该标签时的参数进行校验,根据校验结果处理响应


2.2.1 数据处理流程

1、后端实例化一个空的对象form_obj,交给前端渲染成form表单

2、前端form表单提交数据,数据格式是字典

3、数据字典传给类,实例化生成一个对象,与1中的空对象同名form_obj

4、根据对象form_obj的校验结果返回响应数据

代码:

# 定义类MyForm
# 定义视图函数index
def index(request):
    form_obj = MyForm()
    if request.method == 'POST':
        data = request.POST
        form_obj = MyForm(data)
        if form_obj.is_valid():
            return HttpResponse('数据全部正确')
     return render(request,'index.html',local())

2.2.2 数据校验逻辑

1、通过对象绑定方法is_valid判断数据校验结果是否通过

2、只有所有数据校验通过的情况下,is_valid才为True

3、内部的数据处理逻辑:

  • 从类MyForm定义的字段中,依次去数据字典中比对

  • 若字典中存在同名的key,那么按照字段的参数条件去校验数据字典中key对应的value,不存在就将错误描述加入到form_obj.errors字典中

  • 比对之后,若value符合要求,则加入到form_obj.cleaned_data字典中,若不符合要求,则将错误描述加入到form_obj.errors字典中

  • 比对完所有的类MyForm定义的字段后,结束比对,因此,数据字典多余的数据不会影响校验结果

  • form_obj.errors中,每一个value值都是一个列表,因为每一个字段都可能有多个约束条件

源码分析:

is_valid()

is_bound()

errors

数据校验

2.2.3 常用内置字段及其参数

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类型


Django Form内置字段

2.2.4 常用的几个校验参数

class MyForm(forms.Form):
    name = forms.CharField(
    	lable='给input标签添加一个lable标签,并设置文本内容,默认为字段名'),
    	initial='input框默认值',
        required=True,  # 默认为True,控制标签是否可以为空
        error_messages={
            '本字段已经有的参数名':'校验不通过时的信息描述',
            'required':'不能为空'
        }

2.2.4 内置校验器

from django.core.validators import RegexValidator
from django import forms
class MyForm(forms.Form):
    user = forms.CharField(
        validators=[
        RegexValidator(r'^[0-9]+$', '请输入数字'), 
        RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
    )

2.2.5 钩子函数

局部校验

在MyForm中定义clean_字段名()方法,实现对特定字段的校验

from django.core.exceptions import ValidationError

class MyForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        },
        widget=forms.widgets.TextInput(attrs={"class": "form-control"})
    )
    ...
    # 定义局部钩子,用来校验username字段
    def clean_username(self):
        value = self.cleaned_data.get("username")
        if "666" in value:
            raise ValidationError("光喊666是不行的")
        else:
            return value

全局校验

在MyForm中定义clean()方法,实现对字段的全局校验

class MyForm(forms.Form):
    ...
    password = forms.CharField(
        min_length=6,
        label="密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
    )
    re_password = forms.CharField(
        min_length=6,
        label="确认密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
    )
    ...
    # 定义全局的钩子,用来校验密码和确认密码字段是否相同
    def clean(self):
        password_value = self.cleaned_data.get('password')
        re_password_value = self.cleaned_data.get('re_password')
        if password_value == re_password_value:
            return self.cleaned_data
        else:
            self.add_error('re_password', '两次密码不一致')
            raise ValidationError('两次密码不一致')

2.3 渲染标签

1、forms组件只会渲染获取用户输入的标签,不会渲染提交按钮

2、forms组件应该在form标签内,需要自己写

3、三种渲染方式如下,推荐使用第三种

<p>forms组件渲染标签方式1:封装程度态高 不推荐使用 但是可以用在本地测试</p>
{{ form_obj.as_p }}  <!--自动渲染所有input框  -->
{{ form_obj.as_ul }}
{{ form_obj.as_table }}

<p>forms组件渲染标签方式2:不推荐使用 写起来太烦了</p>
{{ form_obj.username.label }}{{ form_obj.username }}
{{ form_obj.username.label }}{{ form_obj.password }}
{{ form_obj.username.label }}{{ form_obj.email }}

<p>forms组件渲染标签方式3:推荐使用 </p>
{% for form in form_obj %}
	<p>{{ form.label }}{{ form }}</p>  <!--form 等价于你方式2中的对象点字段名-->
{% endfor %}

2.4 展示信息

以校验登陆为例:

# views.py

class MyForm(forms.Form):
    username = forms.CharField(max_length=8, min_length=3, required=True, label='用户名:', error_messages={
        'max_length': '最长8位数',
        'min_length': '最短3位数',
        'required': '不能为空'
    }, widget=forms.widgets.TextInput({'class': 'form-control'}))
    password = forms.CharField(max_length=8, min_length=3, required=True, label='密码:', error_messages={
        'max_length': '最长8位数',
        'min_length': '最短3位数',
        'required': '不能为空'
    }, widget=forms.widgets.PasswordInput({'class': 'form-control'}))
    confir_password = forms.CharField(max_length=8, min_length=3, required=True, label='确认密码:', error_messages={
        'max_length': '最长8位数',
        'min_length': '最短3位数',
        'required': '不能为空'
    }, widget=forms.widgets.PasswordInput({'class': 'form-control'}))
    email = forms.EmailField(label='邮箱:')

    def clean_username(self):
        value = self.cleaned_data.get('username')
        if '666' in value:
            raise ValidationError('6什么6,坐下!')
        else:
            return value

    def clean(self):
        password_value = self.cleaned_data.get('password')
        confir_password_value = self.cleaned_data.get('confir_password')
        if password_value != confir_password_value:
            self.add_error('confir_password', '密码不一致哟')
            raise ValidationError('密码不一致哟')
        else:
            return self.cleaned_data


def index1(request):
    form_obj = MyForm()
    if request.method == 'POST':
        data = request.POST
        form_obj = MyForm(data)
        if form_obj.is_valid():
            return HttpResponse('数据全部正确')
    return render(request, 'index.html', locals())
<!--index.html-->
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <form action="" method="post" novalidate>
                {% for forms in form_obj %}
                    <p>
                        {{ forms.label }}{{ forms }}
                        <span>{{ forms.errors.0 }}</span>
                    </p>  <!--form 等价于你方式2中的对象点字段名-->
                {% endfor %}
                <input type="submit">
            </form>
        </div>
    </div>
</div>

posted @ 2019-12-03 22:55  W文敏W  阅读(254)  评论(0编辑  收藏  举报