zoukankan      html  css  js  c++  java
  • django -- form

    前戏

    我们之前是通过html的form表单来提交数据,提交到服务器之后,我们需要对某些字段做判断,比如用户名密码的长度,格式正确不正确。如果用户输入的内容不正确就要在页面上显示对应的错误信息。当然我们可以通过if..elif来进行判断,但是这样写的话,代码很冗余。而Django的form组件就提供了我们这些校验的功能。

     普通验证

    先来看看不使用form来判断用户名不能小于6位长度是怎么做的

    视图函数

    def register(request):
        error_msg = ''
        if request.method=="POST":
            user = request.POST.get('user')
            pwd = request.POST.get('pwd')
            if len(user)<6:
                error_msg='用户名长度不符合'
            else:
                error_msg = '注册成功'
        return render(request, 'register.html',{'error_msg': error_msg})

    html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="" method="post">
        {% csrf_token %}
        <p>
            用户名:<input type="text" name="user">
        </p>
        <p>
            密码:<input type="text" name="pwd">
        </p>
        <p>
            <input type="submit" value="提交">
            <p style="color: red">{{ error_msg }}</p>
        </p>
    </form>
    
    </body>
    </html>

    使用form组件进行验证

    使用form验证,需要定义一个类,我们在views.py定义一个RegisterForm类

    from django import forms
    
    
    class RegisterForm(forms.Form):  # 继承Form类
        user = forms.CharField(label='用户名')
        pwd = forms.CharField(label='密码')

    在修改视图函数

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

    修改html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="" method="post">
        {% csrf_token %}
        {{ form_obj.as_p }}
        <button>注册</button>
    </form>
    
    </body>
    </html>

    这样,页面就生成了两个input框

    我们可以通过 form_obj.cleaned_data 来获取页面输入的数据,这个必须要放在 form_obj.is_valid() 下面,要不然会报错。

    上面的html文件还可以这样写

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="" method="post">
        {% csrf_token %}
        <p>
            {{ form_obj.user.label }} //只获取label值
            {{ form_obj.user }}   //一个input框
        </p>
        <p>
            {{ form_obj.pwd.label }}
            {{ form_obj.pwd }}
        </p>
        <button>注册</button>
    </form>
    
    </body>
    </html>

    接下来我们给字段加上长度校验,更改RegisterForm

    class RegisterForm(forms.Form):  # 继承Form类
        user = forms.CharField(label='用户名', min_length=6)
        pwd = forms.CharField(label='密码', min_length=6)

    修改html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="" method="post" novalidate>    novalidate不校验
        {% csrf_token %}
        <p>
            {{ form_obj.user.label }}
            {{ form_obj.user }}
        </p>
        <p>
            {{ form_obj.pwd.label }}
            {{ form_obj.pwd }}
        </p>
        <button>注册</button>
        {{ form_obj.errors }}    全局的错误提示
    </form>
    
    </body>
    </html>

    上面的form_obj.errors是全局的校验,用户名和密码都会校验,如果只想校验某个字段,只需要按照下面的方式写

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <form action="" method="post" novalidate>
        {% csrf_token %}
        <p>
            {{ form_obj.user.label }}
            {{ form_obj.user }}
            {{ form_obj.user.errors }}    只对user输入框做校验
            {{ form_obj.user.errors.0 }}  取错误的第一个
        </p>
        <p>
            {{ form_obj.pwd.label }}
            {{ form_obj.pwd }}
        </p>
        <button>注册</button>
    
    </form>
    
    </body>
    </html>

     常用字段和插件

    插件是用于生成HTML的,例如上面生成的input框默认类型是“text”,我们可以使用插件让生成的input框是“password”

    initial

    input框里的默认值

    from django import forms
    
    
    class RegisterForm(forms.Form):  # 继承Form类
        user = forms.CharField(label='用户名',
                               min_length=6,
                               initial='zouzou'
                               )
    error_message

    自定义错误信息

    from django import forms
    
    
    class RegisterForm(forms.Form):  # 继承Form类
        user = forms.CharField(label='用户名',
                               min_length=6,
                               initial='zouzou',
                               error_messages={
                                   "min_length":"长度不符合要求",
                                   "required":"不能为空",
                                   "invalid":"格式错误"
                               }
                               )
    password

    上面生成的input类型是type,我们使用插件来让生成的input标签的类型为pasword

    首先需要导入

    from django.forms import widgets
    from django import forms
    from django.forms import widgets
    
    class RegisterForm(forms.Form):  # 继承Form类
       
        pwd = forms.CharField(label='密码',
                              min_length=6,
                              widget=widgets.PasswordInput()
                              )

    当然,也可以设置属性

    widget=forms.widgets.PasswordInput(attrs={'class': 'c1'})
    ChoiceField
    from django import forms
    from django.forms import widgets
    
    class RegisterForm(forms.Form):  # 继承Form类
       
        gender = forms.ChoiceField(
            choices=(("1",""),("2",""))
        )

    默认是个下拉框,可以添加RadioSelect让它成为单选框

    from django import forms
    from django.forms import widgets
    
    class RegisterForm(forms.Form):  # 继承Form类
       
        gender = forms.ChoiceField(
            choices=(("1",""),("2","")),
            widget=widgets.RadioSelect
        )

    也可以让它成为一个多选

     

    单选checkbox
    from django import forms
    from django.forms import widgets
    
    class RegisterForm(forms.Form):  # 继承Form类
        
        gender = forms.ChoiceField(
            label="是否记住密码",
            initial="checked",
            widget=widgets.CheckboxInput()
        )
    多选checkbox
    from django import forms
    from django.forms import widgets
    
    class RegisterForm(forms.Form):  # 继承Form类
        
        gender = forms.ChoiceField(
            choices=((1, "篮球"), (2, "足球"), (3, "双色球"),),
            label="爱好",
            initial=[1, 3],  # 默认选择1和3
            widget=forms.widgets.CheckboxSelectMultiple()
        )

    上面的是写死的,我们可以从数据库中获取数据

    from django import forms
    from django.forms import widgets
    from appTest01 import models
    class RegisterForm(forms.Form):  # 继承Form类
        
        gender = forms.ChoiceField(
            choices=models.Press.objects.all().values_list('id','name'),  # 从数据库中获取
            label="性别",
            widget=forms.widgets.CheckboxSelectMultiple()
        )

    需要说明的是,这样获取到的数据是项目重启之后再数据库里存在的值,如果项目启动之后你在数据库添加了新的数据,它不会显示在页面上的,如果想显示,我们就要重写构造方法

    from django import forms
    from django.forms import widgets
    from appTest01 import models
    class RegisterForm(forms.Form): # 继承Form类 def __init__(self,*args, **kwargs): super().__init__(*args, **kwargs) self.fields['gender'].choices=models.Press.objects.all().values_list('id','name') gender = forms.ChoiceField( # choices=models.Press.objects.all().values_list('id','name'), # 从数据库中获取 label="爱好", widget=forms.widgets.CheckboxSelectMultiple() )

    我们来看下self.fields是什么

    print(self.fields)
    
    
    OrderedDict([('user', <django.forms.fields.CharField object at 0x048D6B10>), ('pwd', <django.forms.fields.CharField object at 0x048D6B50>), ('gender', <django.forms.fields.ChoiceField object at 0x048D6B90>)])
      1 Field
      2     required=True,               是否允许为空
      3     widget=None,                 HTML插件
      4     label=None,                  用于生成Label标签或显示内容
      5     initial=None,                初始值
      6     help_text='',                帮助信息(在标签旁边显示)
      7     error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
      8     validators=[],               自定义验证规则
      9     localize=False,              是否支持本地化
     10     disabled=False,              是否可以编辑
     11     label_suffix=None            Label内容后缀
     12  
     13  
     14 CharField(Field)
     15     max_length=None,             最大长度
     16     min_length=None,             最小长度
     17     strip=True                   是否移除用户输入空白
     18  
     19 IntegerField(Field)
     20     max_value=None,              最大值
     21     min_value=None,              最小值
     22  
     23 FloatField(IntegerField)
     24     ...
     25  
     26 DecimalField(IntegerField)
     27     max_value=None,              最大值
     28     min_value=None,              最小值
     29     max_digits=None,             总长度
     30     decimal_places=None,         小数位长度
     31  
     32 BaseTemporalField(Field)
     33     input_formats=None          时间格式化   
     34  
     35 DateField(BaseTemporalField)    格式:2015-09-01
     36 TimeField(BaseTemporalField)    格式:11:12
     37 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
     38  
     39 DurationField(Field)            时间间隔:%d %H:%M:%S.%f
     40     ...
     41  
     42 RegexField(CharField)
     43     regex,                      自定制正则表达式
     44     max_length=None,            最大长度
     45     min_length=None,            最小长度
     46     error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
     47  
     48 EmailField(CharField)      
     49     ...
     50  
     51 FileField(Field)
     52     allow_empty_file=False     是否允许空文件
     53  
     54 ImageField(FileField)      
     55     ...
     56     注:需要PIL模块,pip3 install Pillow
     57     以上两个字典使用时,需要注意两点:
     58         - form表单中 enctype="multipart/form-data"
     59         - view函数中 obj = MyForm(request.POST, request.FILES)
     60  
     61 URLField(Field)
     62     ...
     63  
     64  
     65 BooleanField(Field)  
     66     ...
     67  
     68 NullBooleanField(BooleanField)
     69     ...
     70  
     71 ChoiceField(Field)
     72     ...
     73     choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
     74     required=True,             是否必填
     75     widget=None,               插件,默认select插件
     76     label=None,                Label内容
     77     initial=None,              初始值
     78     help_text='',              帮助提示
     79  
     80  
     81 ModelChoiceField(ChoiceField)
     82     ...                        django.forms.models.ModelChoiceField
     83     queryset,                  # 查询数据库中的数据
     84     empty_label="---------",   # 默认空显示内容
     85     to_field_name=None,        # HTML中value的值对应的字段
     86     limit_choices_to=None      # ModelForm中对queryset二次筛选
     87      
     88 ModelMultipleChoiceField(ModelChoiceField)
     89     ...                        django.forms.models.ModelMultipleChoiceField
     90  
     91  
     92      
     93 TypedChoiceField(ChoiceField)
     94     coerce = lambda val: val   对选中的值进行一次转换
     95     empty_value= ''            空值的默认值
     96  
     97 MultipleChoiceField(ChoiceField)
     98     ...
     99  
    100 TypedMultipleChoiceField(MultipleChoiceField)
    101     coerce = lambda val: val   对选中的每一个值进行一次转换
    102     empty_value= ''            空值的默认值
    103  
    104 ComboField(Field)
    105     fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
    106                                fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
    107  
    108 MultiValueField(Field)
    109     PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
    110  
    111 SplitDateTimeField(MultiValueField)
    112     input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    113     input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
    114  
    115 FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    116     path,                      文件夹路径
    117     match=None,                正则匹配
    118     recursive=False,           递归下面的文件夹
    119     allow_files=True,          允许文件
    120     allow_folders=False,       允许文件夹
    121     required=True,
    122     widget=None,
    123     label=None,
    124     initial=None,
    125     help_text=''
    126  
    127 GenericIPAddressField
    128     protocol='both',           both,ipv4,ipv6支持的IP格式
    129     unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
    130  
    131 SlugField(CharField)           数字,字母,下划线,减号(连字符)
    132     ...
    133  
    134 UUIDField(CharField)           uuid类型
    其余的内置字段

    ModelForm

    在上面我们使用form来验证了字段,假如我们注册的时候,数据表里的每个字段都要显示在页面上,难道我们需要一个一个写吗?答案肯定不是的,上面我们的类继承了Form,现在我们继承ModelForm就能达成我们的需求。

    from django import forms
    from crm import models
    from django.core.exceptions import ValidationError
    
    
    # 注册form
    class RegForm(forms.ModelForm):  # 继承ModelForm,比Form功能强大
        '''
        这里重写之后会把Meta里的覆盖,这里如果没写,Meta里写了,Meta里的也不会生效
        '''
        password = forms.CharField(
            label='密码',
            min_length=6,
            widget=forms.widgets.PasswordInput,
            error_messages={
                'min_length': '最小长度为6位',
                'required': '密码不能为空',
            }
        )
    
        re_password = forms.CharField(
            label='确认密码',
            widget=forms.widgets.PasswordInput,
        )
    
        class Meta:  # 展示内容的配置
            model = models.UserProfile  # 用户表
            # fields = '__all__'  # 页面上显示数据表里的所有字段
            # exclude = ['','']  # 页面上不显示某些字段
            fields = ['username', 'password', 're_password', 'name', 'department']  # 页面上指定显示某些字段
            widgets = {   # 使用插件改写密码输入框的type类型
                'username': forms.widgets.EmailInput(attrs={'class':'form-control'}),  # 把username输入框的type改为email,添加一个class属性form-control
                'password': forms.widgets.PasswordInput,  # 改写密码输入框的type类型为password
    
            }
            
            '''
            定义页面显示的内容
            '''
            labels = {
                'username':'用户名',
                'password':"密码",
                're_password': '确认密码',
                'name':'姓名',
                'department':'部门'
    
            }
    
        def __init__(self, *args, **kwargs):  # 获取每一个字段,往里面加class属性
            super().__init__(*args, **kwargs)
            for filed in self.fields.values():
                filed.widget.attrs.update({'class':'form-control'})
        '''
        这里判断密码和确认密码是不是相同,如果不同,给出错误信息
        '''
        def clean(self):
            pwd = self.cleaned_data.get('password')
            re_pwd = self.cleaned_data.get('re_password')
            if pwd == re_pwd:
                return self.cleaned_data
            self.add_error('re_password','两次密码不一致')  # 给re_password添加一个错误信息
            raise ValidationError('两次密码不一致')  # 抛出错误信息

     ModelForm的强大之处不止于此,比如你要编辑一条数据,你是不是需要把这条数据查询出来,在输入框里显示,然后在进行编辑,之前我们查询出来之后,然后一个一个的循环显示在输入框里。ModelForm提供了简单的方法。

    # 编辑客户
    def edit_customer(request, edit_id):
        # 根据id查询出要编辑的客户对象
        obj = models.Customer.objects.filter(id=edit_id).first()
        # 将查询到的对象和对应的html渲染
        form_obj = CustomerForm(instance=obj)  重要
        if request.method == 'POST':
            # 将提交的数据和要修改的实例交给form对象
            form_obj = CustomerForm(request.POST, instance=obj)   重要
            if form_obj.is_valid():
                # 修改后保存
                form_obj.save()
                return redirect(reverse('customer'))
        return render(request,'crm/edit_customer.html',{'form_obj': form_obj})

    说明: form_obj = CustomerForm(request.POST, instance=obj) 里如果没有instance=obj,则是新增

    自定义校验规则

    虽然Django里的Form给我们提供了一些常用的规则,但往往满足不了产品经理的sb需求,这时候就要我们自己定义校验规则了,Django给我们提供了两种校验规则,一种是可以通过正则的方式,另一种是自定义函数。

    通过正则的方式

    先来写个form验证的

    from django import forms
    from django.core.validators import RegexValidator
    
    
    class Phone(forms.Form):
        phone = forms.CharField(
            label='手机号',
            validators=[
                RegexValidator(r'^1[3-9]d{9}$', '手机号格式不正确')
            ]
    
        )

    这里要导入RegexValidator,然后在里面写正则,第一个参数是正则表达式,第二个是错误信息

    在来写对应的视图函数

    from appTest01 import forms
    
    
    def register2(request):
        form_obj = forms.Phone()
        if request.method == 'POST':
            form_obj = forms.Phone(request.POST)
            if form_obj.is_valid():
                return HttpResponse('注册成功')
        return render(request, 'register1.html', {"form_obj": form_obj})

    最后来写html页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="" method="post">
            {% csrf_token %}
            {{ form_obj.phone.label }}
            {{ form_obj.phone }}
            <button>提交</button>
            {{ form_obj.errors.phone.0 }}
        </form>
    </body>
    </html>

    这样我们就通过了正则来验证了我们输入的手机号是不是符合格式

    通过自定义函数来验证

    from django.core.validators import RegexValidator
    
    from django.core.exceptions import ValidationError
    
    def check(value):
        if 'ma' in value:
            raise ValidationError('输入的有非法字符')
    
    class Phone(forms.Form):
        phone = forms.CharField(
            label='手机号',
            validators=[
                check
            ]
    
        )

    其他的地方都不需要改,如果输入框里包含“ma”则认为非法

  • 相关阅读:
    天行健宇宙的生与死
    知识库:maven打包时跳过测试
    oracle 行列转换
    C#格式化字符串
    存储过程的使用
    正则表达式
    怎么防止[SQL注入]
    11个月了.
    开通博客喽,
    别的程序员是怎么读你的简历的
  • 原文地址:https://www.cnblogs.com/zouzou-busy/p/11259935.html
Copyright © 2011-2022 走看看