首页 > 其他 > 详细

form组件

时间:2020-05-02 16:42:18      阅读:59      评论:0      收藏:0      [点我收藏+]

参考1

参考2

一、Form组件介绍 

我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。

与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。

Django form组件就实现了上面所述的功能。

总结一下,其实form组件的主要功能如下:

  • 生成页面可用的HTML标签
  • 对用户提交的数据进行校验
  • 保留上次输入内容

关于校验:
  1. 前端通过JS代码做校验 --> 最好有
  2. 后端做校验 --> 必须要有(因为前端的校验可以被跳过)

1、普通方式手写注册功能 

# views.py文件
def reg(request):
    error = {"pwd": "", "user": ""}
    if request.method == "POST":
        name = request.POST.get("username")
        pwd = request.POST.get("pwd")

        if len(pwd) < 6:
            error["pwd"] = "密码不能小于6位"

    return render(request, "reg.html", {"error": error})
技术分享图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>

<form action="/reg/" method="post">
    {% csrf_token %}
    <p>用户名:
        <input type="text" name="username">
        <span>{{ error.username }}</span>
    </p>
    <p>密码:
        <input type="password" name="pwd">
        <span>{{ error.pwd }}</span>
    </p>
    <p>
        <input type="submit">
    </p>
</form>
</body>
</html>
reg.html

2、使用form组件实现注册功能

 (1) vies.py

先定义好一个RegForm类:

# Django Form组件的使用
from django import forms
class RegForm(forms.Form):
    name = forms.CharField(
        # 校验规则相关
        max_length=16,
        label="用户名",
        error_messages={
            "required": "该字段不能为空",
        },
   )
    pwd = forms.CharField(
        label="密码",
        min_length=6,
        max_length=10,
        error_messages={
            "min_length": "密码不能少于6位!",
            "max_length": "密码最长10位!",
            "required": "该字段不能为空",
        }
    )

再写一个视图函数:

# 使用form组件实现注册方式
def reg2(request):
    form_obj = RegForm()
    if request.method == "POST":
        # 实例化form对象的时候,把post提交过来的数据直接传进去
        form_obj = RegForm(request.POST)
        # 调用form_obj校验数据的方法
        if form_obj.is_valid():
            return HttpResponse("注册成功")
    return render(request, "reg2.html", {"form_obj": form_obj})

(2)reg2.html 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<!--novalidate为去掉浏览器的默认校验-->
<form action="/reg2/" method="post" novalidate>
    {% csrf_token %}
    {{ form_obj.as_p }} <!-- 对象.as_p 生成p标签,里边如果有属性会自动成成input标签 --> 
    <!-- 所有的错误中找pwd对应的错误,显示出来 -->
    {{ form_obj.errors.pwd }}
    <p><input type="submit"></p>
</form> 

 技术分享图片 

看网页效果发现 也验证了form的功能:
• 前端页面是form类的对象生成的                                      -->生成HTML标签功能
• 当用户名和密码输入为空或输错之后 页面都会提示        -->用户提交校验功能
• 当用户输错之后 再次输入 上次的内容还保留在input框   -->保留上次输入内容

 二、Form那些事儿

1、form组件的用法

1. from django import forms
2, 定义一个form类
    class RegForm(forms.Form):
        user = forms.CharField()
        pwd = forms.CharField()
        email = forms.EmailField()

生成HTML:
    3. 实例化一个form对象, 传递到模板语言中
    4. 在目标语言中调用form对象的响应方法和属性
    
    三种方式:
        1. {{ form_obj.as_p }}
      因为是p标签,不能引用bootstrat样式(div标签)
2. 单独写 {{ form_obj.pwd.label }} {{ form_obj.pwd }} 做校验: 1. form_obj = RegForm(request.POST) --request.POST为传过来的数据 2. form_obj.is_valid()

2、常用字段与插件

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

(1)initial

初始值,input框里面的初始值。

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三"  # 设置默认值
    )
    pwd = forms.CharField(min_length=6, label="密码")

(2)error_messages

重写错误信息。

class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        }
    )
    pwd = forms.CharField(min_length=6, label="密码")

(3)password

class LoginForm(forms.Form):
    ...
    pwd = forms.CharField(
        min_length=6,
        label="密码",
        widget=forms.widgets.PasswordInput(attrs={class: c1}, render_value=True)
    )

(4)radioSelect

单radio值为字符串

class LoginForm(forms.Form):
   ...
    gender = forms.fields.ChoiceField(
        choices=((1, ""), (2, ""), (3, "保密")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )

(5)单选Select

class LoginForm(forms.Form):
    ...
    hobby = forms.fields.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )

(6)多选Select

class LoginForm(forms.Form):
    ...
    hobby = forms.fields.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )

(7)单选checkbox

class LoginForm(forms.Form):
    ...
    keep = forms.fields.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )

(8)多选checkbox

class LoginForm(forms.Form):
    ...
    hobby = forms.fields.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )

choice字段注意事项

在使用选择标签时,需要注意choices的选项可以配置从数据库中获取,但是由于是静态字段 获取的值无法实时更新,需要重写构造方法从而实现choice实时更新

方式一:

from django.forms import Form
from django.forms import widgets
from django.forms import fields

 
class MyForm(Form):
 
    user = fields.ChoiceField(
        # choices=((1, ‘上海‘), (2, ‘北京‘),),
        initial=2,
        widget=widgets.Select
    )
 
    def __init__(self, *args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        # self.fields[‘user‘].choices = ((1, ‘上海‘), (2, ‘北京‘),)
        #
        self.fields[user].choices = models.Classes.objects.all().values_list(id,caption)

方式二:

from django import forms
from django.forms import fields
from django.forms import models as form_model

 
class FInfo(forms.Form):
    authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())  # 多选
    # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())  # 单选

2、Django Form所有内置字段

技术分享图片
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内置字段
Django Form内置字段

(1)字段校验

RegexValidator验证器

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
mobile = forms.CharField(
        label="手机",
        # 自己定制校验规则
        validators=[
            RegexValidator(r^[0-9]+$, 手机号必须是数字),
            RegexValidator(r^1[3-9][0-9]{9}$, 手机格式有误)
        ],
    )

(2)自定义验证函数

import re
from django.forms import Form
from django.forms import widgets
from django.forms import 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):
 
    title = fields.CharField(max_length=20,
                            min_length=5,
                            error_messages={required: 标题不能为空,
                                            min_length: 标题最少为5个字符,
                                            max_length: 标题最多为20个字符},
                            widget=widgets.TextInput(attrs={class: "form-control",
                                                          placeholder: 标题5-20个字符}))
 
     # 使用自定义验证规则
    phone = fields.CharField(validators=[mobile_validate, ],
                            error_messages={required: 手机不能为空},
                            widget=widgets.TextInput(attrs={class: "form-control",
                                                          placeholder: u手机号码}))
 
    email = fields.EmailField(required=False,
                            error_messages={required: u邮箱不能为空,invalid: u邮箱格式错误},
                            widget=widgets.TextInput(attrs={class: "form-control", placeholder: u邮箱}))

(3)Hook方法

除了上面两种方式,我们还可以在Form类中定义钩子函数,来实现自定义的验证功能。

局部钩子

我们在Fom类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验。

举个例子:

class LoginForm(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

全局钩子

我们在Fom类中定义 clean() 方法,就能够实现对字段进行全局校验。

class LoginForm(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(两次密码不一致)

 

综合实例: 

技术分享图片
from django.shortcuts import render, HttpResponse
import json
from app01 import models
# Create your views here.

# Django Form组件的使用
from django import forms
from django.forms import widgets
# 导入正则校验模块
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
class RegForm(forms.Form):
    name = forms.CharField(
        # 校验规则相关
        max_length=16,
        label="用户名",
        error_messages={
            "required": "该字段不能为空",
        },
        # widget控制的是生成html代码相关的
        widget=widgets.TextInput(attrs={"class": "form-control"})
    )
    pwd = forms.CharField(
        label="密码",
        min_length=6,
        max_length=10,
        # 告知这个input标签是一个password类型的,render_value=True表示刷新时密码不消失
        widget=widgets.PasswordInput(attrs={"class": "form-control"}, render_value=True),
        error_messages={
            "min_length": "密码不能少于6位!",
            "max_length": "密码最长10位!",
            "required": "该字段不能为空",
        }
    )
    re_pwd = forms.CharField(
        label="确认密码",
        min_length=6,
        max_length=10,
        widget=widgets.PasswordInput(attrs={"class": "form-control"}, render_value=True),
        error_messages={
            "min_length": "密码不能少于6位!",
            "max_length": "密码最长10位!",
            "required": "该字段不能为空",
        }
    )

    email = forms.EmailField(
        label="邮箱",

        widget=widgets.EmailInput(attrs={"class": "form-control"}),
        error_messages={
            "required": "该字段不能为空",
        }
    )
    mobile = forms.CharField(
        label="手机",
        # 自己定制校验规则
        validators=[
            RegexValidator(r^[0-9]+$, 手机号必须是数字),
            RegexValidator(r^1[3-9][0-9]{9}$, 手机格式有误)
        ],
        widget=widgets.TextInput(attrs={"class": "form-control"}),
        error_messages={
            "required": "该字段不能为空",
        }
    )

    city = forms.ChoiceField(
        choices=models.City.objects.all().values_list("id", "name"),
        label="城市",
        initial=1,
        widget=forms.widgets.Select
    )

    # 重写父类的init方法
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["city"].widget.choices = models.City.objects.all().values_list("id", "name")
        #每一次实例化的时候都会自动走一遍init方法,去数据库中查询所有的城市然后赋给widget字段的choices中


    def clean_name(self):
        value = self.cleaned_data.get("name")

        if "sb" in value:
            raise ValidationError("不符合社会主义核心价值观!")
        return value
#
    # 重写父类的clean方法(参考forms.py中def _clean_form(self)函数)
    def clean(self):
        # 此时 通过检验的字段的数据都保存在 self.cleaned_data
        pwd = self.cleaned_data.get("pwd")
        re_pwd = self.cleaned_data.get("re_pwd")
        if pwd != re_pwd:
            #g给固定的字段添加固定的异常
            self.add_error("re_pwd", ValidationError("两次密码不一致"))
            raise ValidationError("两次密码不一致")
        return self.cleaned_data

def reg2(request):
    form_obj = RegForm()   # 实例化一个对象

    if request.method == "POST":
        form_obj = RegForm(request.POST)
        # 让form帮我们做校验
        if form_obj.is_valid():
            # 校验通过
            # 所有经过校验的数据都保存在 form_obj.cleaned_data
            print(form_obj.cleaned_data)  #{‘name‘: ‘aaa‘, ‘pwd‘: ‘123456‘, ‘re_pwd‘: ‘123456‘}
            #确认的密码不需要存到数据库中,删除
            del form_obj.cleaned_data["re_pwd"]
            # 把数据存到数据库
            models.UserInfo.objects.create(**form_obj.cleaned_data)
            return HttpResponse("注册成功!")
        # print(form_obj.errors["__all__"][0])
        # print(form_obj.errors["re_pwd"])

    return render(request, "reg2.html", {"form_obj": form_obj})
views.py 
技术分享图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1 {
            list-style-type: none;
        }
    </style>

    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>


<!-- 自己逐个字段写 -->

<div class="container">
    <div class="row">
        <!--表单居中:左边偏移3,自己占6,右边空3-->
        <div class="col-md-6 col-md-offset-3">
            <form action="/reg2/" method="post" novalidate>
                {% csrf_token %}
                <div class="form-group {% if form_obj.name.errors.0 %}has-error{% endif %}">
                    <!--拿到name这个input标签对应的label标签-->
                    {{ form_obj.name.label }}
                     <!--拿到input标签-->
                    {{ form_obj.name }}
                     <!--errors是自定义的错误列表,根据索引取值-->
                    <span class="help-block">{{ form_obj.name.errors.0 }}</span>
                </div>

                <div class="form-group {% if form_obj.pwd.errors.0 %}has-error{% endif %}">
                    {{ form_obj.pwd.label }}
                    {{ form_obj.pwd }}
                    <span class="help-block">{{ form_obj.pwd.errors.0 }}</span>
                </div>
                <div class="form-group {% if form_obj.re_pwd.errors.0 %}has-error{% endif %}">
                    {{ form_obj.re_pwd.label }}
                    {{ form_obj.re_pwd }}
                    <span class="help-block">{{ form_obj.re_pwd.errors.0 }}</span>
                </div>
                <div class="form-group {% if form_obj.email.errors.0 %}has-error{% endif %}">
                    {{ form_obj.email.label }}
                    {{ form_obj.email }}
                    <span class="help-block">{{ form_obj.email.errors.0 }}</span>
                </div>
                <div class="form-group {% if form_obj.mobile.errors.0 %}has-error{% endif %}">
                    {{ form_obj.mobile.label }}
                    {{ form_obj.mobile }}
                    <span class="help-block">{{ form_obj.mobile.errors.0 }}</span>
                </div>


                <div class="form-group {% if form_obj.city.errors.0 %}has-error{% endif %}">
                    {{ form_obj.city.label }}
                    {{ form_obj.city }}
                    <span class="help-block">{{ form_obj.city.errors.0 }}</span>
                </div>
                <div class="form-group">
                    <input type="submit" class="btn btn-default">
                </div>
            </form>
        </div>
    </div>

</div>


</body>
</html>
reg2.html

技术分享图片

form组件

原文:https://www.cnblogs.com/zh-xiaoyuan/p/12815136.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!