Form组件

手动实现登录校验的功能

前端代码

<form action="" method="post">
    {% csrf_token %}
    <p>username:<input type="text" name="username" value="{{ username }}"><span>{{ back_dic.username }}</span></p>
    <p>password:<input type="text" name="password" value="{{ password }}"><span>{{ back_dic.password }}</span></p>
    <p><input type="submit"></p>
</form>
前端

后端代码

def register(request):
    back_dic = {
        "username":"",
        "password":""
    }
    username = ""
    password = ""
    if request.method == 'POST':
        username = request.POST.get("username")
        password = request.POST.get("password")
        if "666"in username:        # 不符合,就把提示信息加入到字典中,在页面中相应的地方渲染
            back_dic["username"] = "喊6剁手"
        if len(password) < 3:
            back_dic["password"] = "密码太短"

    return render(request, "register.html",locals())
# 除了大字典传参渲染页面之外, locals()也可以传参渲染,并且locals()会把定义的变量全部传过去
后端

上述代码我们一共实现三个功能

  1. 渲染页面

  2. 对数据进行校验

  3. 展示提示信息

form组件实现的功能

form组件主要也是帮我们实现上面三个功能

1. 对数据进行校验

数据校验分为前端校验和后端校验两种,前端校验可以不做,后端校验必须做

因为前端很脆弱,别人可容易就会越过你的校验,如果我们后端不做校验

就会很危险,后端校验如下

# 使用form组件的第一步就是需要我们自己写一个类去继承forms.Form
from django import forms
class MyForm(forms.Form):
    username = forms.CharField(max_length=6, label="用户名")
                            # max_length表示限制条件,  label表示文本信息
    password = forms.CharField(max_length=6, min_length=3, label="密码", error_messages={"min_length":"密码太短"})

def register(request):
    # 生成一个form对象
    form_obj = MyForm()

    if request.method == 'POST':
        # 前端传过来数据之后
        # 1. 给form组件传参,字典的形式
        form_obj = MyForm(request.POST)
        # 2.判断数据是否全部合法 is_valid()
        print(form_obj.is_valid())  # is_valid() 方法会校验你传入的数据是否符合定义的限制
                                    # 只要有一个不符合就会返回False
        # 3. 查看所有校验通过的数据cleaned_data
        print(form_obj.cleaned_data)
        # 4. 查看所有没通过的数据及其报错信息errors
        print(form_obj.errors)
    return render(request, "register.html", locals())

2. 渲染页面

渲染页面主要有三种方式

第一种渲染标签方式(本地)

{{ form_obj.as_p }}

直接通过forms对象点as_p 就可以生成所有被p包裹的标签,

如果点as_ul,就会生成被li包裹的标签,并且在展示时,默认会把label标签的首字母大写

但是这种方法的封装程度太高,不方便我们后期添加样式,所以基本上作本地测试用

第二种渲染标签方式(可扩展性高)

<p>{{ form_obj.username.label }}{{ form_obj.username }}</p>
<p>{{ form_obj.password.label }}{{ form_obj.password }}</p>

我们自己定义标签,通过forms对象点出单独的标签,这种方式的扩展性高

但是如果我们forms对象的字段比较多的话,一个一个点比较麻烦

第三种渲染标签的方式(常用)

{% for foo in form_obj %}
    {{ foo.label }}{{ foo }}
{% endfor %}

我们可以循环forms对象,得到是每一个字段对应的标签,这样我们就可以

通过点语法得到我们想要的标签(扩展性高且不用写太多代码)

3. 展示错误信息

当我们渲染完标签之后,我们会发现当我们输入错误信息的时候,浏览器端

会提示我们报错信息,如下

这种提示错误信息的方式其实是forms组件在前端做了判断,并展示出报错信息

前面我们也提到过,既然后端进行了数据校验,那么我们可不可以自定义提示信息呢

如果我们想展示后端错误信息,那首先需要让前端停止校验

<form action="" method="post" novalidate>

novalidate的意思就是让前端停止校验

首选我们需要在每个字段中添加错误条件及其对应的提示 error_messages

password = forms.CharField(max_length=6, min_length=3, label="密码", 
                           error_messages={
                               "min_length":"密码太短",
                               "max_length":"密码太长"
                                           })

然后我们在前端,可以通过.errors拿到报错信息的一个列表,索引0取出信息

{% for foo in form_obj %}
    <p>{{ foo.label }}{{ foo }} {{ foo.errors }}</p>
{% endfor %}

这样就可以展示出我们自己的提示信息了

还有一点之前忘了补充:

那就是如果我们不用forms组件去提示错误信息,那也会把用户之前输入的

信息全部刷新掉,这种体验是非常差的,而我们的forms组件则会保留之前的信息

form组件的hook函数(钩子函数)

上面展示的错误提示都是一些系统里面自带的校验规则,例如最大、最小长度

那我们想自己定义一些规则,例如用户名不能包含666,这就需要用到钩子函数

钩子函数分为两种,局部钩子与全局钩子

局部钩子

局部钩子只能对单个字段进行二次校验

def clean_username(self):   # 局部钩子通过clean_字段名 定义, 只要在上面出现过的字段,在这都会有提示
    username = self.cleaned_data.get("username")  # 然后我们取值需要从校验成功这里面取值
    if "666" in username: # 进行判断,错误就通过add_error添加到错误信息中
        self.add_error("username", "不能包含666") # add_error("字段名", "错误信息")
    return username # 为了健壮性, 就把username返出去

全局钩子

全局钩子可以对多个字段联合起来校验

def clean(self): # 全局钩子直接通过clean来定义
    password = self.cleaned_data.get("password")
    confirm_password = self.cleaned_data.get("confirm_password")
    if password != confirm_password:
        self.add_error("confirm_password", "两次密码不一致")
    return self.cleaned_data

不管是全局钩子,还是局部钩子,都只有在通过了第一次校验之后,才会进入钩子函数

forms组件类中一些其他字段及参数

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内置字段
字段及参数

具体内容及使用方法

posted @ 2019-07-23 07:25  hesujian  阅读(169)  评论(0编辑  收藏  举报