Django之表单验证

时间:Oct. 14, 2017 分类:

目录:

form表单验证

Django自带表单验证功能。

表单验证的实现

urls路由

urlpatterns = [
    url(r'articles/(?P<year>[0-9]{4})/$', views.year_archive,{ 'mouth': '09' }),
    url(r'add/$', views.add),
    url(r'form1/$', views.form1),
]

views视图(业务逻辑)

from forms import Form1
from django.shortcuts import render
def form1(request):
    if request.method == 'POST':
        request.POST.get('username',None)
        request.POST.get('password',None)
        # 获取请求内容进行验证
        f = Form1(request.POST)
        ret = f.is_valid()
        print ret
    return render(request, 'form.html')

form表单

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django import forms

class Form1(forms.Form):

    name = forms.CharField()
    password = forms.CharField()

template的form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app/form1/" method="post">
        <div>
            <input type="text" name="username">
        </div>
        <div>
            <input type="text" name="password">
        </div>
        <div>
            <input type="submit" value="提交">
        </div>
    </form>

</body>
</html>

获取验证结果

直接什么都不输入进行访问

直接进行访问

后端print的结果

False
[10/Oct/2017 00:13:43] "POST /app/form1/ HTTP/1.1" 200 418

输入字段

True
[10/Oct/2017 00:25:08] "POST /app/form1/ HTTP/1.1" 200 418

获取错误信息

def form1(request):
    if request.method == 'POST':
        request.POST.get('username',None)
        request.POST.get('password',None)
        # 获取请求内容进行验证
        f = Form1(request.POST)
        if f.is_valid():
            print f.cleaned_data
        else:
            print f.errors
            print type(f.errors)
    return render(request, 'form.html')

输入字段

{'username': u'123', 'password': u'123'}
[10/Oct/2017 00:26:36] "POST /app/form1/ HTTP/1.1" 200 418

直接进行访问

<ul class="errorlist"><li>username<ul class="errorlist"><li>This field is required.</li></ul></li><li>password<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
<class 'django.forms.utils.ErrorDict'>
[10/Oct/2017 00:26:47] "POST /app/form1/ HTTP/1.1" 200 418

错误返回到前端

form.html使用一个模板进行接收

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {{ error }}
    <form action="/app/form1/" method="post">
        <div>
            <input type="text" name="username">
        </div>
        <div>
            <input type="text" name="password">
        </div>
        <div>
            <input type="submit" value="提交">
        </div>
    </form>

</body>
</html>

form.py将error通过render传递到前端。

def form1(request):
    if request.method == 'POST':
        request.POST.get('username',None)
        request.POST.get('password',None)
        # 获取请求内容进行验证
        f = Form1(request.POST)
        if f.is_valid():
            print f.cleaned_data
        else:
            print f.errors
            print type(f.errors)
        return render(request, 'form.html',{'error':f.errors})
    return render(request, 'form.html')

当然也可以返回locals(),代表当前所有局部变量

不进行输入,前端和可以直接获取到

不过被封装在ul和li中了,自定义一下

def form1(request):
    if request.method == 'POST':
        request.POST.get('username',None)
        request.POST.get('password',None)
        # 获取请求内容进行验证
        f = Form1(request.POST)
        if f.is_valid():
            print f.cleaned_data
        else:
            print f.errors['username'][0]
            print f.errors['password'][0]
        return render(request, 'form.html',{'error':f.errors})
    return render(request, 'form.html')

可以看到以下情况

This field is required.
This field is required.

有个问题,如果error中没有username这个key。

def form1(request):
    if request.method == 'POST':
        request.POST.get('username',None)
        request.POST.get('password',None)
        # 获取请求内容进行验证
        f = Form1(request.POST)
        if f.is_valid():
            print f.cleaned_data
        else:
            print f.errors.get('username',None)
            print f.errors.get('password',None)
        return render(request, 'form.html',{'error':f.errors})
    return render(request, 'form.html')

None
<ul class="errorlist"><li>This field is required.</li></ul>

分开显示

放到html对应位置

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app/form1/" method="post">
        <div>
            <input type="text" name="username">
            <span>{{ error.username }}</span>
        </div>
        <div>
            <input type="text" name="password">
            <span>{{ error.password }}</span>
        </div>
        <div>
            <input type="submit" value="提交">
        </div>
    </form>

</body>
</html>

进行访问

前端显示

浏览器的前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app/form1/" method="post">
        <div>
            <input type="text" name="username">
            <span>{{ error.username.0 }}</span>
        </div>
        <div>
            <input type="text" name="password">
            <span>{{ error.password.0 }}</span>
        </div>
        <div>
            <input type="submit" value="提交">
        </div>
    </form>

</body>
</html>

添加样式

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .input-group {
            position: relative;
            padding: 20px;
            width: 250px;
        }
        .input-group input{
            width: 200px;
            display: inline-block;
        }
        .input-group span{
            display: inline-block;
            position: absolute;
            height: 12px;
            font-size: 8px;
            border: 1px solid darkred;
            background-color: coral;
            color: white;
            top: 41px;
            left: 20px;
            width: 202px;
        }
    </style>
</head>
<body>
    <form action="/app/form1/" method="post">
        <div class="input-group">
            <input type="text" name="username">
            <span>{{ error.username.0 }}</span>
        </div>
        <div class="input-group">
            <input type="text" name="password">
            <span>{{ error.password.0 }}</span>
        </div>
        <div>
            <input type="submit" value="提交">
        </div>
    </form>

</body>
</html>

前端展示

浏览器前端代码

不异常的时候不进行显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .input-group {
            position: relative;
            padding: 20px;
            width: 250px;
        }
        .input-group input{
            width: 200px;
            display: inline-block;
        }
        .input-group span{
            display: inline-block;
            position: absolute;
            height: 12px;
            font-size: 8px;
            border: 1px solid darkred;
            background-color: coral;
            color: white;
            top: 41px;
            left: 20px;
            width: 202px;
        }
    </style>
</head>
<body>
    <form action="/app/form1/" method="post">
        <div class="input-group">
            <input type="text" name="username">
            {% if error.username.0 %}
            <span>{{ error.username.0 }}</span>
            {% endif %}
        </div>
        <div class="input-group">
            <input type="text" name="password">
            {% if error.password.0 %}
            <span>{{ error.password.0 }}</span>
            {% endif %}
        </div>
        <div>
            <input type="submit" value="提交">
        </div>
    </form>

</body>
</html>

会有一个问题,提交的数据没有通过验证,新的页面刚才添加的数据都没有了

使用form自动生成input标签

def form1(request):
    f = Form1()
    if request.method == 'POST':
        request.POST.get('username',None)
        request.POST.get('password',None)
        # 获取请求内容进行验证
        f = Form1(request.POST)
        return render(request, 'form.html',{'error':f.errors,'form': f})
    return render(request, 'form.html', {'form': f})

当然这里只考虑了验证失败的情况,成功肯定是做跳转了,就省略掉了

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .input-group {
            position: relative;
            padding: 20px;
            width: 250px;
        }
        .input-group input{
            width: 200px;
            display: inline-block;
        }
        .input-group span{
            display: inline-block;
            position: absolute;
            height: 12px;
            font-size: 8px;
            border: 1px solid darkred;
            background-color: coral;
            color: white;
            top: 41px;
            left: 20px;
            width: 202px;
        }
    </style>
</head>
<body>
    <form action="/app/form1/" method="post" novalidate>
        <div class="input-group">
            {{ form.username }}
            {% if error.username.0 %}
            <span>{{ error.username.0 }}</span>
            {% endif %}
        </div>
        <div class="input-group">
            {{ form.password }}
            {% if error.password.0 %}
            <span>{{ error.password.0 }}</span>
            {% endif %}
        </div>
        <div>
            <input type="submit" value="提交">
        </div>
    </form>

</body>
</html>

可以看到自动生成的input标签

对于只输入username的返回页面

注意要有form里要有novalidate,这个是html5给form元素新增了一个novalidate属性,指定为true或者就直接仅仅声明这个属性的时候,不会验证字段

否则会出现以下情况

对于默认required=True,也就是不允许为空

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django import forms

class Form1(forms.Form):

    username = forms.CharField(required=False)
    password = forms.CharField()

字段

  • max_length 最大长度
  • min_length 最小长度
  • error_messages 错误信息
  • required 是否可以为空
  • widget 样式

自定义错误信息

form.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django import forms

class Form1(forms.Form):

    username = forms.CharField(required=False)
    password = forms.CharField()
    email = forms.EmailField(error_messages={'required': '邮箱不能为空','invalid': '邮箱格式错误'})

form.html

        <div class="input-group">
            {{ form.email }}
            {% if error.email.0 %}
            <span>{{ error.email.0 }}</span>
            {% endif %}
        </div>

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django import forms

class Form1(forms.Form):

    username = forms.CharField(required=False)
    password = forms.CharField()
    email = forms.EmailField(error_messages={'required': '邮箱不能为空','invalid': '邮箱格式错误'})

自定义样式

默认情况下为widget=forms.TextInput()

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django import forms

class Form1(forms.Form):

    username = forms.CharField(required=False, widget=forms.Textarea(attrs={'class':'why'}))
    password = forms.CharField()
    email = forms.EmailField(error_messages={'required': '邮箱不能为空','invalid': '邮箱格式错误'})

前端可以看到textarea格式的输入框

前端代码

自定义选项框

可以直接定义选项框数据,但是这样有局限性,可以从数据库拿,下边就直接展示显示从数据库获取数据

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django import forms
from models import UserGroup

class Form1(forms.Form):

    username = forms.CharField(required=False, widget=forms.Textarea(attrs={'class':'why'}))
    password = forms.CharField()
    email = forms.EmailField(error_messages={'required': '邮箱不能为空','invalid': '邮箱格式错误'})
    usertype_choice = UserGroup.objects.values_list("id", "caption")
    print usertype_choice
    usertype = forms.CharField(widget=forms.widgets.Select(choices=usertype_choice, attrs={'class': "form-control"}))

添加字段

        <div class="input-group">
            {{ form.usertype }}
            {% if error.usertype.0 %}
            <span>{{ error.usertype.0 }}</span>
            {% endif %}
        </div>

前端效果

数据库数据

mysql> select * from app_usergroup;
+----+---------+
| id | caption |
+----+---------+
|  1 | ops     |
|  2 | dev     |
|  3 | test    |
+----+---------+
3 rows in set (0.00 sec)

mysql> insert into app_usergroup (caption) values ('admin');
Query OK, 1 row affected (0.01 sec)

mysql> select * from app_usergroup;
+----+---------+
| id | caption |
+----+---------+
|  1 | ops     |
|  2 | dev     |
|  3 | test    |
|  4 | admin   |
+----+---------+
4 rows in set (0.00 sec)

可以看到,添加了新数据,但是前端首页

但是添加了新的数据,没有显示,重启才能生效

因为这个是一个类的静态字段,在进行解释的时候就被调用,生成的时候就被创建,保持不变,所以在数据库中添加了是不会生效,就需要改成一个动态字段,在每次生成类的时候调用一次

下边代码进行了动态的绑定

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django import forms
from models import UserGroup

class Form1(forms.Form):

    username = forms.CharField(required=False, widget=forms.Textarea(attrs={'class':'why'}))
    password = forms.CharField()
    email = forms.EmailField(error_messages={'required': '邮箱不能为空','invalid': '邮箱格式错误'})
    # usertype_choice = UserGroup.objects.values_list("id", "caption")
    # usertype = forms.CharField(widget=forms.widgets.Select(choices=usertype_choice, attrs={'class': "form-control"}))

    def __init__(self, *args, **kwargs):
        super(Form1, self).__init__(*args, **kwargs)

        self.fields['usertype'] = forms.CharField(
            widget=forms.widgets.Select(choices=UserGroup.objects.values_list("id", "caption"), attrs={'class': "form-control"})
        )

再度修改数据库数据

mysql> insert into app_usergroup (caption) values ('member');
Query OK, 1 row affected (0.01 sec)

可以看到新生成的数据已经出现在选项框中

自定义验证规则

        <div class="input-group">
            {{ form.phone }}
            {% if error.phone.0 %}
            <span>{{ error.phone.0 }}</span>
            {% endif %}
        </div>

对于自定义验证信息需要进行方法的创建

#!/usr/bin/env python
# -*- coding:utf-8 -*-

from django import forms
from models import UserGroup
import re
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 Form1(forms.Form):

    username = forms.CharField(required=False, widget=forms.Textarea(attrs={'class':'why'}))
    password = forms.CharField()
    email = forms.EmailField(error_messages={'required': '邮箱不能为空','invalid': '邮箱格式错误'})
    phone = forms.CharField(validators=[mobile_validate, ],
                            error_messages={'required': u'手机不能为空'},
                            widget=forms.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手机号码'}))


    def __init__(self, *args, **kwargs):
        super(Form1, self).__init__(*args, **kwargs)

        self.fields['usertype'] = forms.CharField(
            widget=forms.widgets.Select(choices=UserGroup.objects.values_list("id", "caption"), attrs={'class': "form-control"})
        )

进行错误提交的返回

进行正确提交的返回

对于ajax请求

对于ajax,需要根据error构造合适的返回值了。

参考代码

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
from django import forms
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(forms.Form):

    user_type_choice = (
        (0, u'普通用户'),
        (1, u'高级用户'),
    )

    user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,
                                                                  attrs={'class': "form-control"}))

    title = forms.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': u'标题不能为空',
                                            'min_length': u'标题最少为5个字符',
                                            'max_length': u'标题最多为20个字符'},
                            widget=forms.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'标题5-20个字符'}))

    memo = forms.CharField(required=False,
                           max_length=256,
                           widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'详细描述', 'rows': 3}))

    phone = forms.CharField(validators=[mobile_validate, ],
                            error_messages={'required': u'手机不能为空'},
                            widget=forms.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手机号码'}))

    email = forms.EmailField(required=False,
                            error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
                            widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

代码摘自Python之路【第十七篇】:Django【进阶篇 】

触发Model中的验证和错误提示有两种方式:

  1. Django Admin中的错误信息会优先根据Admin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息
  2. 调用Model对象的 clean_fields 方法

For example:

# models.py
class UserInfo(models.Model):
    nid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=32)

    email = models.EmailField(error_messages={'invalid': '格式错了.'})

# views.py
def index(request):
    obj = models.UserInfo(username='11234', email='uu')
    try:
        print(obj.clean_fields())
    except Exception as e:
        print(e)
    return HttpResponse('ok')

# Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。

Admin中修改错误提示

# admin.py
from django.contrib import admin
from model_club import models
from django import forms


class UserInfoForm(forms.ModelForm):
 username = forms.CharField(error_messages={'required': '用户名不能为空.'})
 email = forms.EmailField(error_messages={'invalid': '邮箱格式错误.'})
 age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'})

 class Meta:
     model = models.UserInfo
     # fields = ('username',)
     fields = "__all__"


class UserInfoAdmin(admin.ModelAdmin):
 form = UserInfoForm


admin.site.register(models.UserInfo, UserInfoAdmin)