django form和form组件

form介绍:

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

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

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

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

    生成页面可用的HTML标签

    对用户提交的数据进行校验

    保留上次输入内容

form的具体使用:

  1. 写一个form组件的实例:

    # 导入相关的包forms
    from django import forms
    
    # 自定义一个类让它继承forms.Form类
    class UserInfo(forms.Form):
        # 写你需要的在前端显示的form内的相关标签,通过对象调用会自动生成
        name = forms.CharField(   
           	# 在前端页面中显示定义的内容,这个属性的名称
            label='姓名:'      
            
            # 自定义错误信息,在每个属性中单独定制,会针对每个标签提交的错误信息返回不同的报错提示信息
            error_messages={
                "required":"不能为空!"    
                "invalid": "格式错误",
                "min_length": "用户名最短8位"
            }
        )
        password = forms.CharField(
            label='密码:',
            widget = forms.widgets.PasswordInput()  # 给这个字段设置属性,输入的时候一密文形式
        )
    

    视图函数的语法操作:

    def index(request):
        # 实例化这个对象,操作类中的属性
        u_obj = UserInfo()
        if request.method == 'GET':
            return render(request,'index.html',{'u_obj':u_obj})   # 将这个对象通过render在前端页面进行渲染
        u_obj = UserInfo(request.POST)   # 前端页面提交过来的数据是:{'name':'xx','password'='123'}
        
        # 校验数据的过程,根据前端提交的数据去类中找对应的属性,进行校验
        if u_obj.is_valid():  # 校验用户提交的数据,全部校验成功返回True,任意一个失败都返回False
            return HttpResponse('ok')
        return render(request, 'index.html', {'u_obj': u_obj})    
    

    前端文件中的操作方式:

    <form action="" novalidate>     <!-- novalidate 告诉前端form表单,不要对输入的内容做校验-->
        <div>
            <!--通过对象直接可以调用类中的属性, u_obj.name.label 显示的是当前这个标签的名称 -->
            <!-- u_obj.name 直接就会生成对应的标签-->
            {{ u_obj.name.label }} {{ u_obj.name }} {{ u_obj.name.errors.0 }}  
            <!-- u_obj.name.errors.0 打印对应的错误信息,由于errors是全部的错误,所以需要 .0 获取第一个-->
        </div>
        <div>
            {{ u_obj.password.label }} {{ u_obj.password }}
        </div>
    </form>
    
  2. form常用字段和插件

    initial

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

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

    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="密码")
    

    password

    class LoginForm(forms.Form):
        ...
        pwd = forms.CharField(
            min_length=6,
            label="密码",
            widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) #这个密码字段和其他字段不一样,默认在前端输入数据错误的时候,点击提交之后,默认是不保存的原来数据的,但是可以通过这个render_value=True让这个字段在前端保留用户输入的数据
        )
    

    radioSelect

    单radio值为字符串

    class LoginForm(forms.Form):
        username = forms.CharField(  #其他选择框或者输入框,基本都是在这个CharField的基础上通过插件来搞的
            min_length=8,
            label="用户名",
            initial="张三",
            error_messages={
                "required": "不能为空",
                "invalid": "格式错误",
                "min_length": "用户名最短8位"
            }
        )
        pwd = forms.CharField(min_length=6, label="密码")
        gender = forms.fields.ChoiceField(
            choices=((1, "男"), (2, "女"), (3, "保密")),
            label="性别",
            initial=3,
            widget=forms.widgets.RadioSelect()
        )
    

    单选Select

    class LoginForm(forms.Form):
        ...
        hobby = forms.fields.ChoiceField(  #注意,单选框用的是ChoiceField,并且里面的插件是Select,不然验证的时候会报错, Select a valid choice的错误。
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
            label="爱好",
            initial=3,
            widget=forms.widgets.Select()
        )
    

    多选Select

    class LoginForm(forms.Form):
        ...
        hobby = forms.fields.MultipleChoiceField( #多选框的时候用MultipleChoiceField,并且里面的插件用的是SelectMultiple,不然验证的时候会报错。
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
            label="爱好",
            initial=[1, 3],
            widget=forms.widgets.SelectMultiple()
        )
    

    单选checkbox

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

    单选checkbox示例:

    #单选的checkbox
        class TestForm2(forms.Form):
            keep = forms.ChoiceField(
                choices=(
                    ('True',1),
                    ('False',0),
                ),
    
                label="是否7天内自动登录",
                initial="1",
                widget=forms.widgets.CheckboxInput(), 
            )
        选中:'True'   #form只是帮我们做校验,校验选择内容的时候,就是看在没在我们的choices里面,里面有这个值,表示合法,没有就不合法
        没选中:'False'
        ---保存到数据库里面  keep:'True'
        if keep == 'True':
            session 设置有效期7天
        else:
            pass
    

    多选checkbox

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

    date类型

    from django import forms
    from django.forms import widgets
    class BookForm(forms.Form):
        date = forms.DateField(widget=widgets.TextInput(attrs={'type':'date'}))  #必须指定type,不然不能渲染成选择时间的input框
    

    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) #注意重写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 = forms.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())  # 多选    #或者下面这种方式,通过forms里面的models中提供的方法也是一样的。
        authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())  # 多选
        #authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())  # 单选    #或者,forms.ModelChoiceField(queryset=models.Publisth.objects.all(),widget=forms.widgets.Select()) 单选    #
       authors = forms.ModelMultipleChoiceField(    queryset=models.Author.objects.all(),    widget = forms.widgets.Select(attrs={'class': 'form-control'}   ))
       #如果用这种方式,别忘了model表中,NNEWType的__str__方法要写上,不然选择框里面是一个个的object对象
    

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类型
复制代码

内置字段

字段校验

RegexValidator验证器

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

自定义验证函数

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):   # 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'邮箱'}))

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)
    )
    ...
    # 定义全局的钩子,用来校验密码和确认密码字段是否相同,执行全局钩子的时候,cleaned_data里面肯定是有了通过前面验证的所有数据
    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', '两次密码不一致') #在re_password这个字段的错误列表中加上一个错误,并且clean_data里面会自动清除这个re_password的值,所以打印clean_data的时候会看不到它
            raise ValidationError('两次密码不一致')
posted @ 2019-10-14 19:52  adrian-boy  阅读(244)  评论(0编辑  收藏  举报