python 全栈开发,Day78(Django组件-forms组件)

python 全栈开发,Day78(Django组件-forms组件)

一、Django组件-forms组件

forms组件

django中的Form组件有以下几个功能:

  • 生成HTML标签
  • 验证用户数据(显示错误信息)
  • HTML Form提交保留上次提交数据
  • 初始化页面显示内容

校验字段功能

之前写的视图函数,提交的数据,没有做校验,就添加到数据库里面了。这样是不对的!

比如:用户名,必须要符合一定的长度。密码复杂度,等等。

forms组件最大的作用,就是做数据校验。

 

普通做法,一个一个写校验规则,没有解耦。校验规则,都在视图函数里面。

新建项目formDemo

修改urls.py,新增路径index

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
]
View Code

修改views.py,新增index视图函数

form组件先放到视图函数
单独起一个类,后续会分离出来

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render

# Create your views here.
from django import forms  # 必须导入模块
class DemoForm(forms.Form):  # 必须继承Form
    #限制数据为字符串,最大长度32
    name = forms.CharField(max_length=32)
    age = forms.IntegerField()  # 限制为数字
    email = forms.EmailField()  # 限制为邮箱格式

def index(request):
    return render(request,"index.html")
View Code

templates新增index.html,里面是空的

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

</body>
</html>
View Code

打开Pycharm,点击左下角的Django Console

python 全栈开发,Day78(Django组件-forms组件)

输入以下命令,导入视图函数的DemoForm类

from app01.views import DemoForm

效果如下:

python 全栈开发,Day78(Django组件-forms组件)

DemoForm类是用来做校验的,它接收一个字典。字典必须包含2个key,分别是name,age,email

测试一个字典数据

执行下面2个命令

form=DemoForm({"name":"xiao","age":"21","email":"[email protected]"})
form.is_valid()

效果如下:输出True

python 全栈开发,Day78(Django组件-forms组件)

解释:

is_valid()表示执行校验,如果3个key都符合要求,输出True

 

测试1:age不符合

python 全栈开发,Day78(Django组件-forms组件)

 

 3个必须同时成立才行python 全栈开发,Day78(Django组件-forms组件)

在DemoForm里面,等式左边对应的是key,等式右边对应校验规则

它有一个默认规则,不能为空

 测试2:少一个字段

python 全栈开发,Day78(Django组件-forms组件)

 

测试3:加一个额外的key-value呢?

python 全栈开发,Day78(Django组件-forms组件)

 从结果上来看,也是可以通过的。

 它只校验指定的字段,那么额外的键值,会忽略。

 

网页校验

修改urls.py,增加路径addbook

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    path('addbook/', views.addbook),
]
View Code

修改views.py,增加addbook视图函数,完整代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render,HttpResponse

# Create your views here.
from django import forms  # 必须导入模块
class UserForm(forms.Form):  # 必须继承Form
    #限制数据为字符串,最小长度4,最大长度12
    name = forms.CharField(min_length=4,max_length=12)
    age = forms.IntegerField()  # 限制为数字
    #限制长度为11位
    tel = forms.CharField(min_length=11,max_length=11)

def index(request):
    return render(request,"index.html")

def addbook(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        if form.is_valid():  # 验证数据
            print("success")
        else:
            print("fail")
        return HttpResponse("ok")

    return render(request,"addbook.html")
View Code

templates新增addbook.html

做表单校验的时候,一定要注意,表单的name和class的属性必须一一对应

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>添加用户</h3>
<form action="" method="post">
    {% csrf_token %}
    <lable>姓名</lable><input type="text" name="name"/><br/>
    <lable>年龄</lable><input type="text" name="age"/><br/>
    <lable>邮箱</lable><input type="text" name="email"/><br/>
    <lable>手机号码</lable><input type="text" name="tel"/>
    <br/>
    <input type="submit">
</form>
</body>
</html>
View Code

网页访问添加页面,输出信息

python 全栈开发,Day78(Django组件-forms组件)

 提交之后,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

Pycharm控制台输出:success

 

空表单直接提交

python 全栈开发,Day78(Django组件-forms组件)

Pycharm控制台输出:fail

 

is_valid()

form.is_valid() 它做了2件事情:

1.将数据传给form
2.将验证数据拆分到2个容器中

self.cleaned_data= {} 表示干净的数据
self.error = {} 表示验证不通过的数据

self表示UserForm类的实例对象

 

addbook视图函数

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
def addbook(request):
    if request.method == "POST":
        print(request.POST)
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)
            print(form.errors)
        else:
            print("###fail###")
            print(form.cleaned_data)
            print(form.errors)
            print(type(form.errors))

        return HttpResponse("ok")

    return render(request,"addbook.html")
View Code

再次提交数据

python 全栈开发,Day78(Django组件-forms组件)

Pycharm控制台输出:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<QueryDict: {'tel': ['12345678910'], 'email': ['[email protected]'], 'name': ['xiao'], 'age': ['23'], 'csrfmiddlewaretoken': ['wv7VhRwG4YvEO7SqE9qsMnpO4RpH1ys1KdiOrwgnrN3WRgW0IH8prXSUMCgdMz7u']}>
###success###
{'tel': '12345678910', 'age': 23, 'name': 'xiao'}

<class 'django.forms.utils.ErrorDict'>
View Code

虽然POST发送了5个键值,但是UserForm只校验3个键值。

form.cleaned_data 输出了3个键值

form.errors 输出的内容空,它的类型为ErrorDict

只要有一个错误,就会走else 

 

错误数据演示

修改views.py,修改UserForm,增加邮箱

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
class UserForm(forms.Form):  # 必须继承Form
    #限制数据为字符串,最小长度4,最大长度12
    name = forms.CharField(min_length=4,max_length=12)
    age = forms.IntegerField()  # 限制为数字
    email = forms.EmailField()  # 限制为邮箱格式
    #限制长度为11位
    tel = forms.CharField(min_length=11,max_length=11)
View Code

输入一个错误的表单

python 全栈开发,Day78(Django组件-forms组件)

Pycharm控制台输出:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
###fail###
{'name': 'awew', 'age': 12, 'tel': '12345678910'}
<ul class="errorlist"><li>email<ul class="errorlist"><li>Enter a valid email address.</li></ul></li></ul>
View Code

form.errors输出了一段Html标签,提示邮箱格式错误

 

提取email错误信息

修改UserForm

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
def addbook(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
        else:
            print("###fail###")
            print(form.cleaned_data)
            print(form.errors)
            # 获取email错误信息,返回一个错误列表,可以切片
            print(form.errors.get("email"))
            # 获取第一个错误信息
            print(form.errors.get("email")[0])

        return HttpResponse("ok")

    return render(request,"addbook.html")
View Code

Pycharm控制台输出:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
###fail###
[06/Jul/2018 22:33:41] "POST /addbook/ HTTP/1.1" 200 2
{'tel': '12345678910', 'age': 12, 'name': 'awew'}
<ul class="errorlist"><li>email<ul class="errorlist"><li>Enter a valid email address.</li></ul></li></ul>
<ul class="errorlist"><li>Enter a valid email address.</li></ul>
Enter a valid email address.
View Code

form.errors.get("email") 可以提取email的错误信息,它返回的是一个错误列表

通过切片,可以获取第一个错误信息

 

渲染标签功能 

渲染方式1

使用自带的模板属性渲染

上面讲的form表单里面的元素,是手动写的。form组件可以帮你实现渲染表单元素!

那么需要渲染哪些元素,取决于UserForm这个自定义类的属性来决定的

举例:

修改addbook视图函数

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
def addbook(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
        else:
            print("###fail###")
            print(form.cleaned_data)
            print(form.errors)
            # 获取email错误信息,返回一个错误列表,可以切片
            print(form.errors.get("email"))
            # 获取第一个错误信息
            print(form.errors.get("email")[0])

        return render(request, "adduser.html", {"form":form})

    else:
        form = UserForm()
        return render(request,"addbook.html",{"form":form})
View Code

修改addbook.html

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>添加用户</h3>
<form action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <br/>
    <input type="submit">
</form>
</body>
</html>
View Code

as_p是一个特殊的属性,常见的有:

  • {{ form.as_table }} 以表格的形式将它们渲染在<tr> 标签中
  • {{ form.as_p }} 将它们渲染在<p> 标签中
  • {{ form.as_ul }} 将它们渲染在<li> 标签中

 

访问页面,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

使用浏览器工具,查看html代码

它使用了P标签来包裹

python 全栈开发,Day78(Django组件-forms组件)

lable的for属性和input的id属性是对应的。id的名字和UserForm类定义的属性是类似的,加了id_前缀。

lable的显示的文字和UserForm类定义的属性是一样的,首字母大写了!

input的name属性和UserForm类定义的属性是一样的

默认自带required属性,不允许内容为空。

minlength的属性来源于UserForm类的定义。

注意:form组件只能渲染表单里面的元素,比如input标签。除此之外,其他的需要手写!

它的样式,太丑了!

 

渲染方式2

使用自定义的标签来包裹form变量

举例:

更改addbook.html

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>添加用户</h3>
<form action="" method="post">
    {% csrf_token %}
    <div>
        <p>姓名</p>
        {{ form.name }}
    </div>
    <div>
        <p>年龄</p>
        {{ form.age }}
    </div>
    <div>
        <p>邮箱</p>
        {{ form.email }}
    </div>
    <div>
        <p>手机号码</p>
        {{ form.tel }}
    </div>
    <input type="submit">
</form>
</body>
</html>
View Code

刷新网页,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

 

渲染方式3

使用for循环渲染

修改addbook.html

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3>添加用户</h3>
<form action="" method="post">
    {% csrf_token %}
    {% for field in form %}
    <div>
        <label for="">{{ field.label }}</label>
        {{ field }}
    </div>
    {% endfor %}
    <input type="submit">
</form>
</body>
</html>
View Code

刷新网页,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

field.label 表示UserForm类定义的属性名。注意:它不是html的label标签!

field 表示input输入框,由forms组件渲染

 

显示中文

将label换成中文,需要增加label属性

修改views.py里面的UserForm类

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
class UserForm(forms.Form):  # 必须继承Form
    #限制数据为字符串,最小长度4,最大长度12
    name = forms.CharField(min_length=4,max_length=12,label="姓名")
    age = forms.IntegerField(label="年龄")  # 限制为数字
    email = forms.EmailField(label="邮箱")  # 限制为邮箱格式
    #限制长度为11位
    tel = forms.CharField(min_length=11,max_length=11,label="手机号码")
View Code

刷新网页,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

 

 美化input输入框

需要使用bootstrap

修改urls.py,修改路径

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    path('adduser/', views.adduser),
]
View Code

修改views.py,将addbook重名为adduser

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
        else:
            print("###fail###")
            print(form.cleaned_data)
            print(form.errors)
            # 获取email错误信息,返回一个错误列表,可以切片
            print(form.errors.get("email"))
            # 获取第一个错误信息
            print(form.errors.get("email")[0])

        return render(request, "adduser.html", {"form":form})

    else:
        form = UserForm()
        return render(request,"adduser.html",{"form":form})
View Code

将addbook.html,重命名为adduser.html

引入bootstrap,代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
        <h3>添加用户</h3>
            <form action="" method="post">
                {% csrf_token %}
                {% for field in form %}
                    <div>
                        <label for="">{{ field.label }}</label>
                        {{ field }}
                    </div>
                {% endfor %}
                <input type="submit" class="btn btn-success btn-sm">
            </form>
        </div>
    </div>
</div>

</body>
</html>
View Code

访问url: http://127.0.0.1:8000/adduser/

效果如下:

 python 全栈开发,Day78(Django组件-forms组件)

这里面input用的还是默认样式,只要给input标签增加class="form-group",就有美化效果!

由于input是form组件渲染的,不能直接添加class,需要在UserForm类里面,指定class

修改UserForm类之前,导入一个模块widgets

Widgets

Widget 是Django 对HTML 输入元素的表示。Widget 负责渲染HTML和提取GET/POST 字典中的数据。

如果你想让某个Widget 实例与其它Widget 看上去不一样,你需要在Widget 对象实例化并赋值给一个表单字段时指定额外的属性(以及可能需要在你的CSS 文件中添加一些规则)

修改views.py,完整代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render,HttpResponse
from django import forms  # 必须导入模块
from django.forms import widgets
# Create your views here.

class UserForm(forms.Form):  # 必须继承Form
    #定义变量,专门给text类型的输入框添加class
    wid = widgets.TextInput(attrs={"class":"form-control"})
    #限制数据为字符串,最小长度4,最大长度12
    name = forms.CharField(min_length=4,max_length=12,label="姓名",widget=wid)
    age = forms.IntegerField(label="年龄",widget=wid)  # 限制为数字
    # 限制为邮箱格式
    email = forms.EmailField(label="邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}))
    #限制长度为11位
    tel = forms.CharField(min_length=11,max_length=11,label="手机号码",widget=wid)

def index(request):
    return render(request,"index.html")

def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
        else:
            print("###fail###")
            print(form.cleaned_data)
            print(form.errors)
            # 获取email错误信息,返回一个错误列表,可以切片
            print(form.errors.get("email"))
            # 获取第一个错误信息
            print(form.errors.get("email")[0])

        return render(request, "adduser.html", {"form":form})

    else:
        form = UserForm()
        return render(request,"adduser.html",{"form":form})
View Code

解释:

widget等式右边,可以指定多种类型的输入框,比如:TextInput,EmailInput,DateInput...

默认是TextInput

attrs 表示设置css样式,它接收一个字典,可以写多个css样式!

 

修改adduser.html

给div增加class="form-group",表示调整上下间距

col-md-offset-2 表示偏移距离

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-2">
        <h3>添加用户</h3><br/>
            <form action="" method="post">
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }}
                    </div>
                {% endfor %}
                <br/>
                <input type="submit" class="btn btn-success btn-sm">
            </form>
        </div>
    </div>
</div>

</body>
</html>
View Code

 

刷新网页,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

 

显示错误与保存输入信息功能

保存输入信息功能

 比如博客园的注册页面,链接如下:

https://account.cnblogs.com/

直接提交空数据,页面会提示

python 全栈开发,Day78(Django组件-forms组件)

那么form组件,也是可以实现这个效果

修改adduser视图函数

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
            # return HttpResponse("添加成功")
        else:
            print("###fail###")
            # print(form.cleaned_data)
            print(form.errors)
            # # 获取email错误信息,返回一个错误列表,可以切片
            # print(form.errors.get("email"))
            # # 获取第一个错误信息
            # print(form.errors.get("email")[0])
        return render(request, "adduser.html")
        # return render(request, "adduser.html", {"form":form})

    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form
        return render(request,"adduser.html",{"form":form})
View Code

直接提交空数据,页面有错误提示

注意:这个提示是bootstrap做的,不是form组件

python 全栈开发,Day78(Django组件-forms组件)

虽然jquery可以直接对表单进行验证,判断为空,或者其他规则。但是客户端浏览器的js代码,是可以跳过验证的。直接提交数据给服务器,如果服务器没有做数据校验,那么将面临风险!

 

修改adduser.html,在form标签后面增加novalidate,表示关闭bootstrap表单验证

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-2">
        <h3>添加用户</h3><br/>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }}
                    </div>
                {% endfor %}
                <br/>
                <input type="submit" class="btn btn-success btn-sm">
            </form>
        </div>
    </div>
</div>

</body>
</html>
View Code

刷新页面,填3个值,最后一个故意不填写

python 全栈开发,Day78(Django组件-forms组件)

点击提交,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

发现刚刚增加的数据,没有了。这样用户体验不好!用户得小心翼翼的输入每一个数据!

查看Pycharm控制台输出:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
###fail###
<ul class="errorlist"><li>tel<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
View Code

发现它走了else的代码,使用render时,没有传值。导致页面为空!

 

修改views.py

给adduser.html传一个form。注意:此时的form变量是带有表单数据的!

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
            return HttpResponse("添加成功")
        else:
            print("###fail###")
            # print(form.cleaned_data)
            print(form.errors)
            # # 获取email错误信息,返回一个错误列表,可以切片
            # print(form.errors.get("email"))
            # # 获取第一个错误信息
            # print(form.errors.get("email")[0])
        return render(request, "adduser.html", {"form":form})

    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form
        return render(request,"adduser.html",{"form":form})
View Code

再次刷新页面,数据就回来了!

python 全栈开发,Day78(Django组件-forms组件)

数据怎么就回来了呢?

因为既然是POST请求,而且携带了POST数据,必然执行了form.is_valid()

虽然没有验证通过,但是执行下面一句代码时

return render(request, "adduser.html", {"form":form})

此时的form是含有POST表单数据的,所以页面才会渲染出来!

注意:当input属性为password时,是不会渲染的!除此之外,其他的表单元素,是可以渲染的

 

提交一个正确的数据

python 全栈开发,Day78(Django组件-forms组件)

提示添加成功

python 全栈开发,Day78(Django组件-forms组件)

 

显示错误信息

约定俗成,使用span标签来显示错误信息

修改adduser.html,增加span标签

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-2">
        <h3>添加用户</h3><br/>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }}<span>{{ field.errors.0 }}</span>
                    </div>
                {% endfor %}
                <br/>
                <input type="submit" class="btn btn-success btn-sm">
            </form>
        </div>
    </div>
</div>

</body>
</html>
View Code

解释:

field.errors 表示错误列表。因为是列表,会有很多错误信息

field.errors.0 表示接收第一个错误信息。一般取第一个即可!

 

那么问题来了,get请求时,比如地址栏访问页面,它是取不到值的。那么span标签是空的,但是不影响页面展示

 

直接提交空数据,页面会有英文提示,它表示此字段不允许为空

python 全栈开发,Day78(Django组件-forms组件)

 

显示黑色,不好看,加一个样式

修改adduser.html,增加样式。pull-right表示右对齐

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <style>
        .error {
            color: red;
        }
    </style>
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-2">
        <h3>添加用户</h3><br/>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }}<span class="error pull-right">{{ field.errors.0 }}</span>
                    </div>
                {% endfor %}
                <br/>
                <input type="submit" class="btn btn-success btn-sm">
            </form>
        </div>
    </div>
</div>

</body>
</html>
View Code

刷新页面,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

错误信息显示中文

显示中文需要在UserForm类中的字段增加error_message属性

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
class UserForm(forms.Form):  # 必须继承Form
    #定义变量,专门给text类型的输入框添加class
    wid = widgets.TextInput(attrs={"class":"form-control"})
    #定义字典,错误信息显示中文
    #限制数据为字符串,最小长度4,最大长度12
    error_hints = {"required":"该字段不能为空"}
    name = forms.CharField(min_length=4,max_length=12,label="姓名",widget=wid,error_messages=error_hints)
    # 限制为数字
    age = forms.IntegerField(label="年龄",widget=wid,error_messages=error_hints)
    # 限制为邮箱格式
    email = forms.EmailField(label="邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}),error_messages=error_hints)
    #限制长度为11位
    tel = forms.CharField(min_length=11,max_length=11,label="手机号码",widget=wid,error_messages=error_hints)
View Code

解释:

error_messages 用来定义错误信息,可以定义多个错误类型!它接收一个字典

required 表示为空时,输出This field is required. 

那么要定义中文时,重新赋值即可。需要显示日文,韩文,法文...的,自己定义吧!

 

重新访问页面,输入第一个值,提交。其它字段会有错误提示!

python 全栈开发,Day78(Django组件-forms组件)

 邮箱输入字符串,提示一段英文信息。不行,得改!

python 全栈开发,Day78(Django组件-forms组件)

修改UserForm类,修改这一行

error_hints = {"required":"该字段不能为空","invalid":"格式错误!"}

重新提示数据

python 全栈开发,Day78(Django组件-forms组件)

核心问题,必须要明白,错误信息为什么会显示出来?

执行is_valid(),就会执行校验动作。如果不通过,那么form变量就会包含错误信息。
通过form组件渲染错误信息,页面就展示出来

 

局部钩子与全局钩子

上面提到的校验规则是forms组件自带的。 它做了一些简单的校验功能,比如判断字符串,纯数字,邮箱等等。

比如要求用户名,必须包含字母和数字。年龄必须要大于18岁,手机号码要以136,137开头...

这些需求,默认的校验规则是做不到的。

我们想要自行设计校验的规则,Django给我们提供了钩子。

先来看一段源码:

if hasattr(self, 'clean_%s' % name):
    value = getattr(self, 'clean_%s' % name)()
    self.cleaned_data[name] = value

这段源码能够设置钩子的来源。

 

局部钩子

导入模块

from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

举例:

要求用户名不能是纯数字

def clean_name(self):
    val = self.cleaned_data.get("name")  # 获取输入的用户名

    if not val.isdigit():  # 判断不是数字类型
        return val
    else:
        raise ValidationError("用户名不能是纯数字")

注意:

clean_name  这个名字是有含义的,不能随便定义。name表示UserForm类的属性。clean表示校验

val 表示用户输入的用户名

val.isdigit() 表示判断输入的是否为数字,必须return 一个值

raise 表示主动报错,必须接ValidationError。

上面这些要求是源代码定义的,具体可以看源代码。

 

views.py,完整代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render,HttpResponse
from django import forms  # 必须导入模块
from django.forms import widgets
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
# Create your views here.

class UserForm(forms.Form):  # 必须继承Form
    #定义变量,专门给text类型的输入框添加class
    wid = widgets.TextInput(attrs={"class":"form-control"})
    #定义字典,错误信息显示中文
    #限制数据为字符串,最小长度4,最大长度12
    error_hints = {"required":"该字段不能为空","invalid":"格式错误!"}
    name = forms.CharField(min_length=4,max_length=12,label="姓名",widget=wid,error_messages=error_hints)
    # 限制为数字
    age = forms.IntegerField(label="年龄",widget=wid,error_messages=error_hints)
    # 限制为邮箱格式
    email = forms.EmailField(label="邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}),error_messages=error_hints)
    #限制长度为11位
    tel = forms.CharField(min_length=11,max_length=11,label="手机号码",widget=wid,error_messages=error_hints)

    def clean_name(self):
        val = self.cleaned_data.get("name")  # 获取输入的用户名

        if not val.isdigit():  # 判断数字类型
            return val
        else:
            raise ValidationError("用户名不能是纯数字")

def index(request):
    return render(request,"index.html")

def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
            return HttpResponse("添加成功")
        else:
            print("###fail###")
            # print(form.cleaned_data)
            print(form.errors)
            # # 获取email错误信息,返回一个错误列表,可以切片
            # print(form.errors.get("email"))
            # # 获取第一个错误信息
            # print(form.errors.get("email")[0])
        return render(request, "adduser.html", {"form":form})

    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form
        return render(request,"adduser.html",{"form":form})
View Code

 

验证一下,输入4位数字,提交之后,页面提示如下:

python 全栈开发,Day78(Django组件-forms组件)

手机号码必须11位

修改UserForm类,增加clean_tel方法,完整代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render,HttpResponse
from django import forms  # 必须导入模块
from django.forms import widgets
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
# Create your views here.

class UserForm(forms.Form):  # 必须继承Form
    #定义变量,专门给text类型的输入框添加class
    wid = widgets.TextInput(attrs={"class":"form-control"})
    #定义字典,错误信息显示中文
    #限制数据为字符串,最小长度4,最大长度12
    error_hints = {"required":"该字段不能为空","invalid":"格式错误!"}
    name = forms.CharField(min_length=4,max_length=12,label="姓名",widget=wid,error_messages=error_hints)
    # 限制为数字
    age = forms.IntegerField(label="年龄",widget=wid,error_messages=error_hints)
    # 限制为邮箱格式
    email = forms.EmailField(label="邮箱",widget=widgets.EmailInput(attrs={"class":"form-control"}),error_messages=error_hints)
    #限制长度为11位
    tel = forms.CharField(max_length=11,label="手机号码",widget=wid,error_messages=error_hints)

    def clean_name(self):  # 校验name值
        val = self.cleaned_data.get("name")  # 获取输入的用户名

        if not val.isdigit():  # 判断数字类型
            return val
        else:
            raise ValidationError("用户名不能是纯数字")

    def clean_tel(self):
        val = self.cleaned_data.get("tel")
        if len(val) == 11:  # 判断长度
            return val
        else:
            raise ValidationError("手机号码必须11位")


def index(request):
    return render(request,"index.html")

def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
            return HttpResponse("添加成功")
        else:
            print("###fail###")
            # print(form.cleaned_data)
            print(form.errors)
            # # 获取email错误信息,返回一个错误列表,可以切片
            # print(form.errors.get("email"))
            # # 获取第一个错误信息
            # print(form.errors.get("email")[0])
        return render(request, "adduser.html", {"form":form})

    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form
        return render(request,"adduser.html",{"form":form})
View Code

注意:要去除tel里面的min_length,这是不严谨的写法!

 

重新访问页面,测试一下

python 全栈开发,Day78(Django组件-forms组件)

 

 年龄必须18岁以上

增加clean_age方法

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
    def clean_age(self):
        val = self.cleaned_data.get("age")
        if int(val) > 18:  # input输入的值为字符串,必须转换为int
            return val
        else:
            raise ValidationError("年龄必须满18岁以上!")
View Code

重新访问页面,测试一下

python 全栈开发,Day78(Django组件-forms组件)

注意:

is_valid执行时,才会执行校验。

这里有2层校验。第一层校验是UserForm定义的那些属性,比如判断字符串或者数字的。

第二次校验是clean_属性名 定义的这些方法。只有通过第一层校验时,才会进入第二层校验。

不论式第一层还是第二层,通过校验后,将key_value放到 cleaned_data容器里面。不通过校验时,将key-value放到errors容器里面

 

查看源代码

先找到views.py里面的is_valid,使用Ctrl+鼠标左键,点击is_valid。它会调转到is_valid方法的源代码

点击self.errors-->full_clean()-->self._clean_fields()

_clean_fields源代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
    def _clean_fields(self):
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            if field.disabled:
                value = self.get_initial_for_field(field, name)
            else:
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)
View Code

它会对表单的每一个数据,使用for循环处理。field指的是UserForm定义的那些属性

self.fields.items() 这里的self.fields数据,可能是这样的。

self.fields={"name":name的field对象,"age":age的field对象...}

self.fields.items() ,就能拿到一个field对象。这个field是是一个规则对象.

 

self.cleaned_data[name] = value 表示通过第一层校验,将干净的数据放到cleaned_data容器里

if hasattr(self, 'clean_%s' % name): 表示进入第二校验,name表示UserForm定义的那些属性。通过后,也会放到cleaned_data容器里

except ValidationError as e  表示接收ValidationError错误,add_error表示添加到error容器里。

所以在UserForm定义clean时,必须使用raise ValidationError(xxx)

 

假设通过第一层校验,但是没有通过第二层校验时。它会执行add_error方法,那么原来的cleaned_data容器的数据怎么办?

add_error源代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
    def add_error(self, field, error):
        """
        Update the content of `self._errors`.

        The `field` argument is the name of the field to which the errors
        should be added. If it's None, treat the errors as NON_FIELD_ERRORS.

        The `error` argument can be a single error, a list of errors, or a
        dictionary that maps field names to lists of errors. An "error" can be
        either a simple string or an instance of ValidationError with its
        message attribute set and a "list or dictionary" can be an actual
        `list` or `dict` or an instance of ValidationError with its
        `error_list` or `error_dict` attribute set.

        If `error` is a dictionary, the `field` argument *must* be None and
        errors will be added to the fields that correspond to the keys of the
        dictionary.
        """
        if not isinstance(error, ValidationError):
            # Normalize to ValidationError and let its constructor
            # do the hard work of making sense of the input.
            error = ValidationError(error)

        if hasattr(error, 'error_dict'):
            if field is not None:
                raise TypeError(
                    "The argument `field` must be `None` when the `error` "
                    "argument contains errors for multiple fields."
                )
            else:
                error = error.error_dict
        else:
            error = {field or NON_FIELD_ERRORS: error.error_list}

        for field, error_list in error.items():
            if field not in self.errors:
                if field != NON_FIELD_ERRORS and field not in self.fields:
                    raise ValueError(
                        "'%s' has no field named '%s'." % (self.__class__.__name__, field))
                if field == NON_FIELD_ERRORS:
                    self._errors[field] = self.error_class(error_class='nonfield')
                else:
                    self._errors[field] = self.error_class()
            self._errors[field].extend(error_list)
            if field in self.cleaned_data:
                del self.cleaned_data[field]
View Code

注意:看最后一行,它会将cleaned_data容器里,没通过的数据给删除掉!

 

全局钩子

局部钩子只能校验一个字段,那么2个字段校验呢?比如密码和确认密码必须一致,这个时候,需要使用全局钩子

如何定义全局钩子呢?查看源代码

is_valid()-->self.errors-->self.full_clean()-->self._clean_form()-->self.clean()

clean源代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
    def clean(self):
        """
        Hook for doing any extra form-wide cleaning after Field.clean() has been
        called on every field. Any ValidationError raised by this method will
        not be associated with a particular field; it will have a special-case
        association with the field named '__all__'.
        """
        return self.cleaned_data
View Code

谷歌翻译如下:

在Field.clean()之后进行任何额外的表单范围清理,呼吁每个领域。此方法引发的任何ValidationError都将与特定领域无关;它将有一个特例与名为'__all__'的字段关联。

大概意思就是,它在clean_xx执行之后,才会执行。一旦引发了ValidationError,与特定领域无关。错误信息都在'__all__'里面

这个clean是全局钩子,属于第3层校验规则。源代码没有任何逻辑,所以这个方法,需要我们来重写。注意:名字必须是clean,结尾部分必须是return self.cleaned_data

 

两次密码不一致

修改UserForm类,增加2个属性,并定义全局钩子clean

完整代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render, HttpResponse
from django import forms  # 必须导入模块
from django.forms import widgets
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError


# Create your views here.

class UserForm(forms.Form):  # 必须继承Form
    # 定义变量,专门给text类型的输入框添加class
    wid = widgets.TextInput(attrs={"class": "form-control"})
    # 定义字典,错误信息显示中文
    # 限制数据为字符串,最小长度4,最大长度12
    error_hints = {"required": "该字段不能为空", "invalid": "格式错误!"}
    name = forms.CharField(min_length=4, max_length=12, label="姓名", widget=wid, error_messages=error_hints)
    # 密码字段
    pwd = forms.CharField(label="密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}))

    r_pwd = forms.CharField(label="确认密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}))
    # 限制为数字
    age = forms.IntegerField(label="年龄", widget=wid, error_messages=error_hints)
    # 限制为邮箱格式
    email = forms.EmailField(label="邮箱", widget=widgets.EmailInput(attrs={"class": "form-control"}),
                             error_messages=error_hints)
    # 限制长度为11位
    tel = forms.CharField(max_length=11, label="手机号码", widget=wid, error_messages=error_hints)

    def clean_name(self):  # 校验name值
        val = self.cleaned_data.get("name")  # 获取输入的用户名

        if not val.isdigit():  # 判断数字类型
            return val
        else:
            raise ValidationError("用户名不能是纯数字")

    def clean_tel(self):
        val = self.cleaned_data.get("tel")
        if len(val) == 11:  # 判断长度
            return val
        else:
            raise ValidationError("手机号码必须11位")

    def clean_age(self):
        val = self.cleaned_data.get("age")
        if int(val) > 18:  # input输入的值为字符串,必须转换为int
            return val
        else:
            raise ValidationError("年龄必须满18岁以上!")

    def clean(self):  # 全局钩子
        pwd = self.cleaned_data.get("pwd")
        r_pwd = self.cleaned_data.get("r_pwd")
        if pwd and r_pwd and pwd != r_pwd:  # 判断2次密码不为空,并且2次密码不相等
            raise ValidationError("两次密码不一致")
        else:
            return self.cleaned_data  # 这句是固定写法,不能变动


def index(request):
    return render(request, "index.html")


def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
            return HttpResponse("添加成功")
        else:
            print("###fail###")
            # print(form.cleaned_data)
            print(form.errors)
            # # 获取email错误信息,返回一个错误列表,可以切片
            # print(form.errors.get("email"))
            # # 获取第一个错误信息
            # print(form.errors.get("email")[0])
        return render(request, "adduser.html", {"form": form})

    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form
        return render(request, "adduser.html", {"form": form})
View Code

 

重新访问页面,效果如下:

python 全栈开发,Day78(Django组件-forms组件)

测试2次密码不一致

python 全栈开发,Day78(Django组件-forms组件)

发现密码没有错误提示,为什么呢?

因为全局钩子和局部钩子不一样,它的错误信息在__all__里面

修改adduser视图函数,完整代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render, HttpResponse
from django import forms  # 必须导入模块
from django.forms import widgets
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError


# Create your views here.

class UserForm(forms.Form):  # 必须继承Form
    # 定义变量,专门给text类型的输入框添加class
    wid = widgets.TextInput(attrs={"class": "form-control"})
    # 定义字典,错误信息显示中文
    # 限制数据为字符串,最小长度4,最大长度12
    error_hints = {"required": "该字段不能为空", "invalid": "格式错误!"}
    name = forms.CharField(min_length=4, max_length=12, label="姓名", widget=wid, error_messages=error_hints)
    # 密码字段
    pwd = forms.CharField(label="密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}),
                          error_messages=error_hints)

    r_pwd = forms.CharField(label="确认密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}),
                            error_messages=error_hints)
    # 限制为数字
    age = forms.IntegerField(label="年龄", widget=wid, error_messages=error_hints)
    # 限制为邮箱格式
    email = forms.EmailField(label="邮箱", widget=widgets.EmailInput(attrs={"class": "form-control"}),
                             error_messages=error_hints)
    # 限制长度为11位
    tel = forms.CharField(max_length=11, label="手机号码", widget=wid, error_messages=error_hints)

    def clean_name(self):  # 校验name值
        val = self.cleaned_data.get("name")  # 获取输入的用户名

        if not val.isdigit():  # 判断数字类型
            return val
        else:
            raise ValidationError("用户名不能是纯数字")

    def clean_tel(self):
        val = self.cleaned_data.get("tel")
        if len(val) == 11:  # 判断长度
            return val
        else:
            raise ValidationError("手机号码必须11位")

    def clean_age(self):
        val = self.cleaned_data.get("age")
        if int(val) > 18:  # input输入的值为字符串,必须转换为int
            return val
        else:
            raise ValidationError("年龄必须满18岁以上!")

    def clean(self):  # 全局钩子
        pwd = self.cleaned_data.get("pwd")
        r_pwd = self.cleaned_data.get("r_pwd")
        if pwd and r_pwd and pwd != r_pwd:  # 判断2次密码不为空,并且2次密码不相等
            raise ValidationError("两次密码不一致")
        else:
            return self.cleaned_data  # 这句是固定写法,不能变动


def index(request):
    return render(request, "index.html")


def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
            return HttpResponse("添加成功")
        else:
            print("###fail###")
            # print(form.cleaned_data)
            print(form.errors)
            # # 获取email错误信息,返回一个错误列表,可以切片
            # print(form.errors.get("email"))
            # # 获取第一个错误信息
            # print(form.errors.get("email")[0])
            g_error = form.errors.get("__all__")  # 接收全局钩子错误信息
            if g_error:  # 判断有错误信息的情况下
                g_error = g_error[0]  # 取第一个错误信息

            # 将form和g_error变量传给adduser.html
            return render(request, "adduser.html", {"form": form, "g_error": g_error})

    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form
        return render(request, "adduser.html", {"form": form})
View Code

修改adduser.html,完整代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <style>
        .error {
            color: red;
        }
    </style>
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-2">
        <h3>添加用户</h3><br/>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span class="error pull-right">{{ field.errors.0 }}</span>
                         {% if field.label == "确认密码" %}
                         <span class="error pull-right">{{ g_error|default_if_none:"" }}</span>
                         {% endif %}
                    </div>
                {% endfor %}
                <br/>
                <input type="submit" class="btn btn-success btn-sm">
            </form>
        </div>
    </div>
</div>

</body>
</html>
View Code

解释:

我们想在第二次密码输入框下面,展示全局钩子的错误信息。那么需要用到if判断了

g_error|default_if_none:"" 表示当g_error为none时,页面显示为空("")

 

验证一下,输入2个不一致的密码

 python 全栈开发,Day78(Django组件-forms组件)

效果如下:

python 全栈开发,Day78(Django组件-forms组件)

Pycharm控制台输出:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<QueryDict: {'name': [''], 'r_pwd': ['111'], 'email': [''], 'age': [''], 'pwd': ['11'], 'csrfmiddlewaretoken': ['slo8iY8aB1Z1x6coSPfaqxdrLQW5NSCxG3z1sDSRYQxjAfgYWnX757GxtBNByTh0'], 'tel': ['']}>
###fail###
<ul class="errorlist"><li>__all__<ul class="errorlist nonfield"><li>两次密码不一致</li></ul></li><li>name<ul class="errorlist"><li>该字段不能为空</li></ul></li><li>age<ul class="errorlist"><li>该字段不能为空</li></ul></li><li>tel<ul class="errorlist"><li>该字段不能为空</li></ul></li><li>email<ul class="errorlist"><li>该字段不能为空</li></ul></li></ul>
View Code

 

如果只输入了1个密码呢?
python 全栈开发,Day78(Django组件-forms组件)

页面提示确认密码不能为空

python 全栈开发,Day78(Django组件-forms组件)

两次密码必须输入时,才会进入全局钩子

 这是为什么?此时的UserForm有3层校验规则。执行顺序如下:

forms组件自带的校验规则-->局部钩子-->全局钩子

那么当有一个密码没有输入时,直接被第一层校验规则拦截了,它是不会进入到第三层校验规则的!

设置全局钩子,必然会执行。如果上层报错,那么不会进入全局钩子!

 

思考问题:forms的校验规则和models.py的模型类,有没有关系?

答案是没有关系!forms可以独立运行,forms组件没有必要,必须和model表的字段一一对应。

根据业务需求,在需要校验的字段上,进行校验!

 

分离forms代码

在views.py同级目录创建文件form.py,将forms相关代码剪贴过去,完整内容如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django import forms  # 必须导入模块
from django.forms import widgets
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

class UserForm(forms.Form):  # 必须继承Form
    # 定义变量,专门给text类型的输入框添加class
    wid = widgets.TextInput(attrs={"class": "form-control"})
    # 定义字典,错误信息显示中文
    # 限制数据为字符串,最小长度4,最大长度12
    error_hints = {"required": "该字段不能为空", "invalid": "格式错误!"}
    name = forms.CharField(min_length=4, max_length=12, label="姓名", widget=wid, error_messages=error_hints)
    # 密码字段
    pwd = forms.CharField(label="密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}),
                          error_messages=error_hints)

    r_pwd = forms.CharField(label="确认密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}),
                            error_messages=error_hints)
    # 限制为数字
    age = forms.IntegerField(label="年龄", widget=wid, error_messages=error_hints)
    # 限制为邮箱格式
    email = forms.EmailField(label="邮箱", widget=widgets.EmailInput(attrs={"class": "form-control"}),
                             error_messages=error_hints)
    # 限制长度为11位
    tel = forms.CharField(max_length=11, label="手机号码", widget=wid, error_messages=error_hints)

    def clean_name(self):  # 校验name值
        val = self.cleaned_data.get("name")  # 获取输入的用户名

        if not val.isdigit():  # 判断数字类型
            return val
        else:
            raise ValidationError("用户名不能是纯数字")

    def clean_tel(self):
        val = self.cleaned_data.get("tel")
        if len(val) == 11:  # 判断长度
            return val
        else:
            raise ValidationError("手机号码必须11位")

    def clean_age(self):
        val = self.cleaned_data.get("age")
        if int(val) > 18:  # input输入的值为字符串,必须转换为int
            return val
        else:
            raise ValidationError("年龄必须满18岁以上!")

    def clean(self):  # 全局钩子
        pwd = self.cleaned_data.get("pwd")
        r_pwd = self.cleaned_data.get("r_pwd")
        if pwd and r_pwd and pwd != r_pwd:  # 判断2次密码不为空,并且2次密码不相等
            raise ValidationError("两次密码不一致")
        else:
            return self.cleaned_data  # 这句是固定写法,不能变动
View Code

修改views.py,导入UserForm类,完整代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render, HttpResponse
from app01.form import UserForm  # 导入UserForm类

# Create your views here.
def index(request):
    return render(request, "index.html")

def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印
            return HttpResponse("添加成功")
        else:
            print("###fail###")
            # print(form.cleaned_data)
            print(form.errors)
            # # 获取email错误信息,返回一个错误列表,可以切片
            # print(form.errors.get("email"))
            # # 获取第一个错误信息
            # print(form.errors.get("email")[0])
            g_error = form.errors.get("__all__")  # 接收全局钩子错误信息
            if g_error:  # 判断有错误信息的情况下
                g_error = g_error[0]  # 取第一个错误信息

            # 将form和g_error变量传给adduser.html
            return render(request, "adduser.html", {"form": form, "g_error": g_error})

    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form
        return render(request, "adduser.html", {"form": form})
View Code

再次访问页面,测试密码不一致

python 全栈开发,Day78(Django组件-forms组件)

效果如下:

python 全栈开发,Day78(Django组件-forms组件)

 

form组件补充知识

Django内置字段

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
    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类型
    ...
View Code

你可以在里面选择属性的类型以及约束。

 

Django内置插件

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
View Code

在witgits中选择使用

 

常用插件选择

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
# 单radio,值为字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
 
# 单radio,值为字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.RadioSelect
# )
 
# 单select,值为字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
 
# 单select,值为字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.Select
# )
 
# 多选select,值为列表
# user = fields.MultipleChoiceField(
#     choices=((1,'上海'),(2,'北京'),),
#     initial=[1,],
#     widget=widgets.SelectMultiple
# )
 
 
# 单checkbox
# user = fields.CharField(
#     widget=widgets.CheckboxInput()
# )
 
 
# 多选checkbox,值为列表
# user = fields.MultipleChoiceField(
#     initial=[2, ],
#     choices=((1, '上海'), (2, '北京'),),
#     widget=widgets.CheckboxSelectMultiple
# )
View Code

 

周末作业:

在图书管理系统里,增加一个注册页面

要求:

1.基于forms组件做校验
  1.1 用户名不能小于4位,不能是纯数字,用户名不能重复
  1.2 密码不能小于6位,不能是纯数字
  1.3 两次密码必须一致

2.表单由forms组件渲染

3.显示错误信息

 

进阶功能
使用ajax+forms组件完成注册功能

ajax接收error信息,修改dom,来显示错误信息!

 

答案

使用form表单实现

作业提到的3点要求,在将全局钩子的时候,已经演示出来了。

那么只要合格之后,在视图函数中插入一条记录到用户表中,就可以实现功能了!

下面介绍在上面演示的项目基础上,实现这些功能

修改models.py,增加一个用户表

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    last_time = models.DateTimeField()
View Code

修改settings.py,注册app。最后一行添加应用名

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',
]
View Code

使用下面2个命令生成表

python manage.py makemigrations
python manage.py migrate

手动增加一条记录

python 全栈开发,Day78(Django组件-forms组件)

 

修改form.py,代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django import forms  # 必须导入模块
from django.forms import widgets
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
from app01.models import User  # 导入user表

class UserForm(forms.Form):  # 必须继承Form
    # 定义变量,专门给text类型的输入框添加class
    wid = widgets.TextInput(attrs={"class": "form-control"})
    # 定义字典,错误信息显示中文
    # 限制数据为字符串,最小长度4,最大长度12
    error_hints = {"required": "该字段不能为空", "invalid": "格式错误!"}
    name = forms.CharField(max_length=12, label="姓名", widget=wid, error_messages=error_hints)
    # 密码字段
    pwd = forms.CharField(label="密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}),
                          error_messages=error_hints)

    r_pwd = forms.CharField(label="确认密码", widget=widgets.PasswordInput(attrs={"class": "form-control"}),
                            error_messages=error_hints)

    def clean_name(self):  # 校验name值
        val = self.cleaned_data.get("name")  # 获取输入的用户名
        if len(val) >= 4:  # 判断用户名长度
            if val.isdigit() is False:  # 判断用户名不是纯数字
                if not User.objects.filter(name=val).exists():  # 判断用户名是否存在
                    return val  # 返回正确的值
                else:
                    raise ValidationError("用户名已存在")
            else:
                raise ValidationError("用户名不能为纯数字")
        else:
            raise ValidationError("用户名长度不能小于4位")

    def clean_pwd(self):  # 校验pwd值
        val = self.cleaned_data.get("pwd")  # 获取输入的密码
        if len(val) >= 6:  # 判断密码长度
            if val.isdigit() is False:  # 判断密码不是纯数字
                return val  # 返回正确的值
            else:
                raise ValidationError("密码不能为纯数字")
        else:
            raise ValidationError("密码长度不能小于6位")


    def clean(self):  # 全局钩子
        pwd = self.cleaned_data.get("pwd")
        r_pwd = self.cleaned_data.get("r_pwd")
        if pwd and r_pwd and pwd != r_pwd:  # 判断2次密码不为空,并且2次密码不相等
            raise ValidationError("两次密码不一致")
        else:
            return self.cleaned_data  # 这句是固定写法,不能变动
View Code

修改views.py,代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render, HttpResponse,redirect
from app01.form import UserForm  # 导入UserForm类
from app01.models import User
import datetime

# Create your views here.
def index(request):
    return render(request, "index.html")

def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
        print(request.POST)
        if form.is_valid():  # 验证数据
            print("###success###")
            print(form.cleaned_data)  # 所有干净的字段以及对应的值
            # ErrorDict : {"校验错误的字段":["错误信息",]}
            print(form.errors)
            print(type(form.errors))  # 打印

            name = request.POST.get("name")
            pwd = request.POST.get("pwd")
            last_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            ret = User.objects.create(name=name,pwd=pwd,last_time=last_time)
            if ret:
                # return HttpResponse("添加成功")
                return redirect('/index/')

        else:
            print("###fail###")
            # print(form.cleaned_data)
            print(form.errors)
            # # 获取email错误信息,返回一个错误列表,可以切片
            # print(form.errors.get("email"))
            # # 获取第一个错误信息
            # print(form.errors.get("email")[0])
            g_error = form.errors.get("__all__")  # 接收全局钩子错误信息
            if g_error:  # 判断有错误信息的情况下
                g_error = g_error[0]  # 取第一个错误信息

            # 将form和g_error变量传给adduser.html
            return render(request, "adduser.html", {"form": form, "g_error": g_error})

    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form
    return render(request, "adduser.html",{"form": form})
View Code

修改adduser.html,代码如下:

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <style>
        .error {
            color: red;
        }
    </style>
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-2">
        <h3>添加用户</h3><br/>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span class="error pull-right">{{ field.errors.0 }}</span>
                         {% if field.label == "确认密码" %}
                         <span class="error pull-right">{{ g_error|default_if_none:"" }}</span>
                         {% endif %}
                    </div>
                {% endfor %}
                <br/>
                <input type="submit" class="btn btn-success btn-sm">
            </form>
        </div>
    </div>
</div>

</body>
</html>
View Code

访问页面添加用户界面

python 全栈开发,Day78(Django组件-forms组件)

 查看用户表记录,发现多了一条

python 全栈开发,Day78(Django组件-forms组件)

 

ajax+forms组件实现

在上面的代码上,增加ajax功能

修改urls.py,增加路径add_ajajx

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
    path('adduser/', views.adduser),
    path('add_ajax/', views.add_ajax),
]
View Code

修改views.py,增加视图函数add_ajajx

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
from django.shortcuts import render, HttpResponse,redirect
from app01.form import UserForm  # 导入UserForm类
from app01.models import User
import datetime
import json

# Create your views here.
def index(request):
    return render(request, "index.html")

def adduser(request):
    if request.method == "POST":
        # 将post数据传给UserForm
        form = UserForm(request.POST)
    else:  # 默认是get请求(地址栏输入访问时)
        form = UserForm()  # 没有表单数据的form

    return render(request, "adduser.html",{"form": form})


def add_ajax(request):
    if request.method == "POST": # 判断POST请求
        print(request.POST)
        form = UserForm(request.POST)  #
        result = {"state": False,"name":"","pwd":"","r_pwd":""}
        if form.is_valid():
            name = request.POST.get("name")
            pwd = request.POST.get("pwd")
            last_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            ret = User.objects.create(name=name, pwd=pwd, last_time=last_time)
            if ret:
                result["state"] = True
            return HttpResponse(json.dumps(result,ensure_ascii=False))
        else:
            print(form.errors)
            if form.errors:  # 判断有错误信息的情况下
                if form.errors.get("name"):
                    result["name"] = form.errors.get("name")[0]
                if form.errors.get("pwd"):
                    result["pwd"] = form.errors.get("pwd")[0]
                if form.errors.get("r_pwd"):
                    result["r_pwd"] = form.errors.get("r_pwd")[0]

                g_error = form.errors.get("__all__")  # 接收全局钩子错误信息
                if g_error:  # 判断有错误信息的情况下
                    g_error = g_error[0]  # 取第一个错误信息
                    result["r_pwd"] = g_error

                return HttpResponse(json.dumps(result,ensure_ascii=False))
View Code

修改adduser.html

python 全栈开发,Day78(Django组件-forms组件)python 全栈开发,Day78(Django组件-forms组件)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css">
    <style>
        .error {
            color: red;
        }

        .col-center-block {

            position: absolute;
            top: 50%;
            left: 18%;
            -webkit-transform: translateY(-50%);
            -moz-transform: translateY(-50%);
            -ms-transform: translateY(-50%);
            -o-transform: translateY(-50%);
            transform: translateY(-50%);
        }


    </style>
</head>
<body>
{% csrf_token %}
<div class="container col-center-block">
    <div class="row ">
        <div class="col-md-6 col-md-offset-2">
            <h3>添加用户</h3><br/>
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span class="error pull-right">{{ field.errors.0 }}</span>
                        {% if field.label == "确认密码" %}
                            <span class="error pull-right">{{ g_error|default_if_none:"" }}</span>
                        {% endif %}
                    </div>
                {% endfor %}
                <br/>
                <input type="button" class="btn btn-success btn-sm" id="sub" value="注册">
            </form>
        </div>
    </div>
</div>
<script src="/static/js/jquery.min.js"></script>
{#sweetalert插件#}
<script src="http://mishengqiang.com/sweetalert/js/sweetalert-dev.js"></script>
<link rel="stylesheet" href="http://mishengqiang.com/sweetalert/css/sweetalert.css">
<script>
    $(function () {
        $("#id_name").blur(function () {
            var csrf = $("[name=csrfmiddlewaretoken]").val();  //csrf
            var name = $("#id_name").val();  //用户名
            if (name.length != 0) {
                $.ajax({
                    url: "/zhuce_ajax/",
                    type: "post",
                    data: {
                        'name': name,
                        csrfmiddlewaretoken: csrf,
                    },
                    success: function (data) {
                        var data = JSON.parse(data);  //反序列化数据
                        console.log(data);
                        if (data.name) { //判断用户是否有错误信息
                            $("#id_name").next().text(data.name)  //修改span标签的文本
                        } else {
                            $("#id_name").next().text("")  //验证通过后,清空文件
                        }
                    }

                });
            }

        });
        $("#id_pwd").blur(function () {
            var csrf = $("[name=csrfmiddlewaretoken]").val();  //csrf
            var pwd = $("#id_pwd").val();  //密码
            if (pwd.length != 0) {
                $.ajax({
                    url: "/zhuce_ajax/",
                    type: "post",
                    data: {
                        'name': name,
                        'pwd': pwd,
                        csrfmiddlewaretoken: csrf,
                    },
                    success: function (data) {
                        var data = JSON.parse(data);  //反序列化数据
                        console.log(data);
                        if (data.pwd) { //判断密码是否有错误信息
                            $("#id_pwd").next().text(data.pwd)  //修改span标签的文本
                        } else {
                            $("#id_pwd").next().text("")  //验证通过后,清空文件
                        }
                    }

                });
            }

        });
        $("#id_r_pwd").blur(function () {
            var csrf = $("[name=csrfmiddlewaretoken]").val();  //csrf
            var pwd = $("#id_pwd").val();  //密码
            var r_pwd = $("#id_r_pwd").val();  //确认密码
            if (r_pwd.length != 0) {
                $.ajax({
                    url: "/zhuce_ajax/",
                    type: "post",
                    data: {
                        'name': name,
                        'pwd': pwd,
                        'r_pwd': r_pwd,
                        csrfmiddlewaretoken: csrf,
                    },
                    success: function (data) {
                        var data = JSON.parse(data);  //反序列化数据
                        console.log(data);
                        if (data.r_pwd) { //判断确认密码是否有错误信息
                            $("#id_r_pwd").next().text(data.r_pwd)  //修改span标签的文本
                        } else {
                            $("#id_r_pwd").next().text("")  //验证通过后,清空文件
                        }
                    }

                });
            }

        });

        $("#sub").click(function () {
            var csrf = $("[name=csrfmiddlewaretoken]").val();  //csrf
            var name = $("#id_name").val();  //用户名
            var pwd = $("#id_pwd").val();  //密码
            var r_pwd = $("#id_r_pwd").val();  //确认密码
            $.ajax({
                url: "/zhuce_ajax/",  //请求的url
                type: "post", //默认get
                data: {
                    name: name,
                    pwd: pwd,
                    r_pwd: r_pwd,
                    csrfmiddlewaretoken: csrf
                },
                success: function (data) {  //data接收响应体,必须要有
                    var data = JSON.parse(data);  //反序列化数据
                    {#console.log(data.state);#}
                    {#console.log(data);  //打印响应体#}
                    if (data.state) {
                        console.log("注册成功");
                        swal({
                            title: '注册成功',
                            type: 'success',  //展示成功的图片
                            timer: 500,  //延时500毫秒
                            showConfirmButton: false  //关闭确认框
                        }, function () {
                            window.location.href = "/index/";  //跳转首页
                        });
                    }
                    else {
                        console.log("注册失败");
                        if (data.name) { //判断用户是否有错误信息
                            $("#id_name").next().text(data.name)  //修改span标签的文本
                        } else {
                            $("#id_name").next().text("")  //验证通过后,清空文件
                        }
                        if (data.pwd) {
                            $("#id_pwd").next().text(data.pwd)
                        } else {
                            $("#id_pwd").next().text("")
                        }
                        if (data.r_pwd) {
                            $("#id_r_pwd").next().text(data.r_pwd)
                        } else {
                            $("#id_r_pwd").next().text("")
                        }

                    }
                }
            })


        })

    })
</script>

</body>
</html>
View Code

 

访问页面:

python 全栈开发,Day78(Django组件-forms组件)

测试效果如下:

python 全栈开发,Day78(Django组件-forms组件)

 

 查看用户表,发现多了一条记录

python 全栈开发,Day78(Django组件-forms组件)

 

完整代码,请参考github

https://github.com/py3study/bms_multi

 

posted @ 2018-07-06 20:17 肖祥 阅读(...) 评论(...) 编辑 收藏