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”则认为非法

 

posted @ 2019-08-07 18:52  邹邹很busy。  阅读(265)  评论(0编辑  收藏  举报