Django之Form、ModelForm 组件

 

【Django的Form组件】

Django的Form主要具有一下几大功能:

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

Form类的使用:

from django.db import models

class UserInfo(models.Model):

    user=models.CharField(max_length=32)
    pwd=models.CharField(max_length=32)
    email=models.CharField(max_length=32)
models.py
from django.shortcuts import render,HttpResponse
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

from django import forms
from django.forms import widgets

class UserForm(forms.Form):
    msg={"required":"该字段不能为空"}
    user=forms.CharField(min_length=5,
                         label="用户名",
                         error_messages=msg,
                         widget=widgets.TextInput(attrs={"class":"form-control"})
                         )
    pwd=forms.CharField(error_messages=msg,
                           label="密码",
                         min_length=5,
                          widget=widgets.PasswordInput(attrs={"class":"form-control"})
                           )
    r_pwd = forms.CharField(error_messages=msg,
                            min_length=5,
                          label="确认密码",
                          widget=widgets.PasswordInput(attrs={"class": "form-control"})
                          )
    email=forms.EmailField(error_messages={"invalid":"邮箱格式错误"},
                           label="邮箱",
                           widget=widgets.EmailInput(attrs={"class":"form-control"})
                           )

    def clean_user(self):
        val=self.cleaned_data.get("user")
        ret=UserInfo.objects.filter(user=val).first()
        if not ret:
            return val
        else:
            raise ValidationError("用户名已存在!")

    def clean_pwd(self):

        val=self.cleaned_data.get("pwd")
        if val.isdigit():
            raise ValidationError("密码不能是纯数字!")
        else:
            return val

    def clean(self):

        pwd=self.cleaned_data.get("pwd")
        r_pwd=self.cleaned_data.get("r_pwd")

        if pwd and r_pwd:
            if pwd==r_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("两次密码不一致!")
        else:
            return self.cleaned_data





from app01.models import UserInfo
def reg(request):
    if request.method=="POST":
        # 数据校验
        form=UserForm(request.POST)
        if form.is_valid():
            print(form.cleaned_data)
            UserInfo.objects.create(**form.cleaned_data)
            return HttpResponse("OK")
        else:
            # print(form.cleaned_data)
            # print(form.errors) # {"user":["",]}
            # print(form.errors.get("user")[0]) # {"user":["",]}

            errors=form.errors

            print("------>",form.errors.get("__all__"))

            if form.errors.get("__all__"):
            g_error=form.errors.get("__all__")[0]

            return render(request, "reg.html",locals())

    else:
         form=UserForm()
         return render(request,"reg.html",locals())
views.py(包括钩子)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        .error{
            color: red;
            margin-left: 12px;
        }
    </style>

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

</head>
<body>

{#<form action="" method="post">#}
{#    {% csrf_token %}#}
{#    <p> 用户名 <input type="text" name="user"><span class="error">{{ errors.user.0 }}</span> </p>#}
{#    <p>密码 <input type="password" name="pwd"><span class="error">{{ errors.pwd.0 }}</span></p>#}
{#    <p>邮箱 <input type="text" name="email"><span class="error">{{ errors.email.0 }}</span></p>#}
{#    <input type="submit">#}
{#</form>#}

<hr>
{#<form action="" method="post">#}
{#    {% csrf_token %}#}
{#    {{ form.as_p }}#}
{##}
{#    <input type="submit">#}
{#</form>#}

<hr>
{#<div class="container">#}
{#    <div class="row">#}
{#        <div class="col-md-8 col-md-offset-2">#}
{#            <form action="" method="post" novalidate>#}
{#                {% csrf_token %}#}
{#                <p> 用户名 {{ form.user }}<span class="error">{{ errors.user.0 }}</span> </p>#}
{#                <p>密码 {{ form.pwd }}<span class="error">{{ errors.pwd.0 }}</span></p>#}
{#                <p>邮箱 {{ form.email }}<span class="error">{{ errors.email.0 }}</span></p>#}
{##}
{#                <input type="submit" class="btn btn-success pull-right">#}
{#            </form>#}
{#        </div>#}
{#    </div>#}
{#</div>#}

<hr>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span class="error">{{ field.errors.0 }}</span>
                        {% if field.label == "确认密码" %}
                           <span class="error">{{ g_error|default:"" }}</span>
                        {% endif %}
                    </div>
                {% endfor %}
                <input type="reset">
                <input type="submit" class="btn btn-success pull-right">
            </form>
        </div>
    </div>
</div>


</body>
</html>
reg.html(包括3种forms渲染html的方式)

 

局部钩子函数  def clean_字段名(self):pass
     def clean_user(self):
        val=self.cleaned_data.get("user")
        ret=UserInfo.objects.filter(user=val).first()
        if not ret:
            return val
        else:
            raise ValidationError("用户名已存在!")
     def clean_pwd(self):
        val=self.cleaned_data.get("pwd")
        if val.isdigit():
            raise ValidationError("密码不能是纯数字!")
        else:
            return val

全局钩子函数  def clean(self):pass   return self.cleaned_data
              注意__all__
     def clean(self):
        pwd=self.cleaned_data.get("pwd")
        r_pwd=self.cleaned_data.get("r_pwd")

        if pwd and r_pwd:
            if pwd==r_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("两次密码不一致!")
        else:
            return self.cleaned_data



from django.shortcuts import render,HttpResponse
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError

from django import forms
from django.forms import widgets

class UserForm(forms.Form):
    msg={"required":"该字段不能为空"}
    user=forms.CharField(min_length=5,
                         label="用户名",
                         error_messages=msg,
                         widget=widgets.TextInput(attrs={"class":"form-control"})
                         )
    pwd=forms.CharField(error_messages=msg,
                           label="密码",
                         min_length=5,
                          widget=widgets.PasswordInput(attrs={"class":"form-control"})
                           )
    r_pwd = forms.CharField(error_messages=msg,
                            min_length=5,
                          label="确认密码",
                          widget=widgets.PasswordInput(attrs={"class": "form-control"})
                          )
    email=forms.EmailField(error_messages={"invalid":"邮箱格式错误"},
                           label="邮箱",
                           widget=widgets.EmailInput(attrs={"class":"form-control"})
                           )
    #局部钩子函数
    def clean_user(self):
        val=self.cleaned_data.get("user")
        ret=UserInfo.objects.filter(user=val).first()
        if not ret:
            return val
        else:
            raise ValidationError("用户名已存在!")
    #局部钩子函数
    def clean_pwd(self):

        val=self.cleaned_data.get("pwd")
        if val.isdigit():
            raise ValidationError("密码不能是纯数字!")
        else:
            return val
    #全局钩子函数
    def clean(self):
        pwd=self.cleaned_data.get("pwd")
        r_pwd=self.cleaned_data.get("r_pwd")

        if pwd and r_pwd:
            if pwd==r_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("两次密码不一致!")
        else:
            return self.cleaned_data


from app01.models import UserInfo
def reg(request):
    if request.method=="POST":
        # 数据校验
        form=UserForm(request.POST)
        if form.is_valid():
            print(form.cleaned_data)
            UserInfo.objects.create(**form.cleaned_data)
            return HttpResponse("OK")
        else:
            # print(form.cleaned_data)
            # print(form.errors) # {"user":["",]}
            # print(form.errors.get("user")[0]) # {"user":["",]}

            errors=form.errors

            print("------>",form.errors.get("__all__"))

            if form.errors.get("__all__"):
                g_error=form.errors.get("__all__")[0]

            return render(request, "reg.html",locals())

    else:
         form=UserForm()
         return render(request,"reg.html",locals())
钩子函数

 登录注册示例:

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login, name='login'),
    path('reg/', views.reg, name='reg'),
    path('getvalidcode/', views.getvalidcode, name='getvalidcode'),
]
urls.py
from django import forms
from django.forms import widgets
from app01.models import UserInfo
from django.core.exceptions import ValidationError
import re

class UserForm(forms.Form):
    user = forms.CharField(min_length=4, label='用户名')
    pwd = forms.CharField(min_length=5, label='密码', widget=widgets.PasswordInput())
    r_pwd = forms.CharField(min_length=5, label='确认密码', widget=widgets.PasswordInput())
    email = forms.CharField(min_length=5, label='邮箱')
    
    #为所有字段加属性
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for filed in self.fields.values():
            filed.widget.attrs.update({'class': 'form-control'})

    def clean_user(self):
        val = self.cleaned_data.get('user')
        user = UserInfo.objects.filter(username=val).first()
        if user:
            raise ValidationError('用户已存在!')
        else:
            return val

    def clean_pwd(self):
        val = self.cleaned_data.get('pwd')
        if val.isdigit():
            raise ValidationError('密码不能是纯数字!')
        else:
            return val

    def clean_email(self):
        val = self.cleaned_data.get('email')
        if re.search('\w+@163.com$', val):
            return val
        else:
            raise ValidationError('邮箱必须是163邮箱!')

    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:
            self.add_error("r_pwd", ValidationError("两次密码不一致!"))  #将全局钩子的错误的键由__all__改为自定义的值
        else:
            return self.cleaned_data
app01/app01_forms.py
from django.db import models
from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
    tel=models.CharField(max_length=32)



#注意settings中加上   AUTH_USER_MODEL = "app01.UserInfo"
models.py(包含自定义auth_user)
from django.shortcuts import render, HttpResponse
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import random
from django.http import JsonResponse
from django.contrib import auth
from app01.app01_froms import UserForm
from app01.models import UserInfo

def login(request):
    if request.is_ajax():
        res = {'user': None, 'err_msg': ''}
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        validcode = request.POST.get('validcode')
        if validcode.upper() == request.session.get('keep_str').upper():
            user_obj = auth.authenticate(username=user, password=pwd)
            if user_obj:
                res['user'] = user
            else:
                res['err_msg'] = '用户名或者密码错误!'
        else:
            res['err_msg'] = '验证码错误'
        return JsonResponse(res)
    else:
        return render(request, 'login.html')

def reg(request):
    if request.method == 'POST':
        res = {'user': None, 'err_msg': ''}
        form = UserForm(request.POST)
        if form.is_valid():
            res['user'] = form.cleaned_data.get('user')
            user = form.cleaned_data.get('user')
            pwd = form.cleaned_data.get('pwd')
            email = form.cleaned_data.get('email')
            UserInfo.objects.create_user(username=user, password=pwd, email=email)
        else:
            res['err_msg'] = form.errors
        return JsonResponse(res)
    else:
        form = UserForm()
        return render(request, 'reg.html', locals())

def get_random_color():
    return (random.randint(0,255),random.randint(0,255),random.randint(0,255))

def getvalidcode(request):
    img = Image.new("RGB", (360, 35), get_random_color())   #新建一个图片对象
    draw = ImageDraw.Draw(img)     #根据图片对象新建一个画笔
    font = ImageFont.truetype("static/font/kumo.ttf", 32)   #字体对象

    keep_str = ""  #验证码
    for i in range(6):
        random_num = str(random.randint(0, 9))   #随机数字
        random_lowalf = chr(random.randint(97, 122))   #随机小写字母
        random_upperalf = chr(random.randint(65, 90))   #随机大写字母
        random_char = random.choice([random_num, random_lowalf, random_upperalf])
        draw.text((i*30+50, 0), random_char, get_random_color(), font=font)   #在图片上写入文本
        keep_str += random_char

    width = 360
    height = 35
    # 在验证码图片上随机加10条线
    # for i in range(10):
    #     x1 = random.randint(0, width)
    #     x2 = random.randint(0, width)
    #     y1 = random.randint(0, height)
    #     y2 = random.randint(0, height)
    #     draw.line((x1, y1, x2, y2), fill=get_random_color())

    # 在验证码图片上随机加50个点
    # for i in range(50):
    #     draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
    #     x = random.randint(0, width)
    #     y = random.randint(0, height)
    #     draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
    # 写与读
    f = BytesIO()   #创建一块内存空间
    img.save(f, "png")    #将图片保存到内存中
    data = f.getvalue()   #读取图片

    # 将验证码存在各自的session中
    request.session['keep_str'] = keep_str
    return HttpResponse(data)
views.py(包含验证码的创建和检验)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css">
</head>
<body>
<h3>注册页面</h3>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <form action="">
            {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="">{{ field.label }}</label>
                        {{ field }}
                        <span class="error pull-right"></span>
                    </div>
                {% endfor %}
                <input type="button" value="注册" class="reg-btn btn btn-primary pull-right login-btn">
            </form>
        </div>
    </div>
</div>
{% csrf_token %}
<script src="/static/js/jquery-3.4.0.js"></script>
<script>
$('.reg-btn').click(function () {
    $.ajax({
        url:'{% url "reg" %}',
        type:'post',
        data:{
            user:$('#id_user').val(),
            pwd:$('#id_pwd').val(),
            r_pwd:$('#id_r_pwd').val(),
            email:$('#id_email').val(),
            csrfmiddlewaretoken:$('[name="csrfmiddlewaretoken"]').val()
        },
        success:function (response) {
            console.log(response);
            if(response.user){
                location.href='{% url "login" %}'
            }else{
                $('.error').html('');
                $(".form-group").removeClass("has-error");
                $.each(response.err_msg,function (i,j) {
                    $('#id_'+i).next().html(j[0]).css('color','red').parent().addClass("has-error")
                })
            }
        }
    })
})
</script>
</body>
</html>
reg.html(包括ajax请求提交数据和循环在相应字段下面显示对应的错误信息)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.css">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <form action="">
                <div class="form-group">
                    <label for="username">用户名</label>
                    <input type="text" id="username" class="form-control">
                </div>
                <div class="form-group">
                    <label for="pwd">密码</label>
                    <input type="password" id="pwd" class="form-control">
                </div>
                <div class="form-group">
                    <label for="validcode">验证码</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" id="validcode" class="form-control">
                        </div>
                        <div class="col-md-6">
                            <img id="img" height="35" width="360" src="{% url 'getvalidcode' %}" alt="">
                        </div>
                    </div>
                </div>
                <input type="button" value="登录" class="btn btn-primary pull-right login-btn">
                <span class="error"></span>
            </form>
        </div>
    </div>
</div>
{% csrf_token %}
<script src="/static/js/jquery-3.4.0.js"></script>
<script>
    $('.login-btn').click(function () {
        $.ajax({
            url:'{% url "login" %}',
            type:'post',
            data:{
                user:$('#username').val(),
                pwd:$('#pwd').val(),
                validcode:$('#validcode').val(),
                csrfmiddlewaretoken:$('[name="csrfmiddlewaretoken"]').val()
            },
            success:function (response) {
                if(response.user){
                    location.href='http://www.baidu.com'
                }else {
                    $('.error').html(response.err_msg).css('color', 'red')
                }
            }
        })
    });
    $('#img').click(function () {
        this.src+='?'
    })
</script>
</body>
</html>
login.html(包含ajax请求提交数据和验证码的刷新)

 

 

1.choices的使用
        class UserInfo(AbstractUser):
            tel=models.CharField(max_length=32)
            gender=models.IntegerField(choices=((1,""),(2,"")),default=1)
             
        yuan=UserInfo.objects.get(pk=1)
     yuan.gender #1 yuan.get_gender_display()
#'男' 注:get_字段名_display()

 

1、定义规则:

1
2
3
4
from django.forms import Form
from django.forms import fields
class xxx(Form):
    xx = fields.CharField(max_lenghth=,min_lenghth=,required=True,error_message=)

2、使用:

1
2
3
4
5
6
7
8
9
10
11
12
obj = xxx(request.POST)
# 是否校验成功
v = obj.is_valid()
    # html标签name属性 = Form类字段名
 
obj.is_valid()验证通过返回True,失败则返回False
 
# 所有错误信息
obj.errors
 
# 正确信息
obj.cleaned_data

登录和注册案例:

 1 from django.shortcuts import render,HttpResponse,redirect
 2 
 3 from django.forms import Form
 4 from django.forms import fields
 5 class LoginForm(Form):
 6     # 正则验证: 不能为空,6-18
 7     username = fields.CharField(
 8         max_length=18,
 9         min_length=6,
10         required=True,
11         error_messages={
12             'required': '用户名不能为空',
13             'min_length': '太短了',
14             'max_length': '太长了',
15         }
16     )
17     # 正则验证: 不能为空,16+
18     password = fields.CharField(min_length=16,required=True)
19     # email = fields.EmailField()
20     # email = fields.GenericIPAddressField()
21     # email = fields.IntegerField()
22 
23 
24 def login(request):
25     if request.method == "GET":
26         return render(request,'login.html')
27     else:
28        obj = LoginForm(request.POST)
29        if obj.is_valid():
30            # 用户输入格式正确
31            print(obj.cleaned_data) # 字典类型
32            return redirect('http://www.baidu.com')
33        else:
34            # 用户输入格式错误
35            return render(request,'login.html',{'obj':obj})
36 
37 views.py
View Code
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 
 9 <form method="POST" action="/login/">
10     {% csrf_token %}
11     用户名:<input type="text" name="username">{{ obj.errors.username.0 }}<br>
12     密码 &nbsp;&nbsp;:<input type="password" name="password">{{ obj.errors.password.0 }}<br>
13     <input type="submit" value="提交">
14 
15 </form>
16 
17 </body>
18 </html>
19 
20 login.html
View Code

基于Form和Ajax提交实现用户登录案例:两种验证方式

  1 from django.shortcuts import render,redirect,HttpResponse
  2 from django.forms import Form
  3 from django.forms import fields
  4 from django.forms import widgets
  5 
  6 class LoginForm(Form):
  7     user = fields.CharField(required=True)
  8     pwd = fields.CharField(min_length=18)
  9 
 10 
 11 def login(request):
 12     if request.method == 'GET':
 13         return render(request,'login.html')
 14     else:
 15         obj = LoginForm(request.POST)
 16         if obj.is_valid():
 17             print(obj.cleaned_data)
 18             return redirect('http://www.baidu.com')
 19         return render(request,'login.html',{'obj': obj})
 20 
 21 def ajax_login(request):
 22     import json
 23     ret = {'status': True,'msg': None}
 24     obj = LoginForm(request.POST)
 25     if obj.is_valid():
 26         print(obj.cleaned_data)
 27     else:
 28         # print(obj.errors) # obj.errors对象
 29         ret['status'] = False
 30         ret['msg'] = obj.errors
 31     v = json.dumps(ret)
 32     return HttpResponse(v)
 33 
 34 #
 35 # class TestForm(Form):
 36 #     t1 = fields.CharField(
 37 #         required=True,
 38 #         max_length=8,
 39 #         min_length=2,
 40 #         error_messages={
 41 #             'required': '不能为空',
 42 #             'max_length': '太长',
 43 #             'min_length': '太短',
 44 #         }
 45 #     )
 46 #     t2 = fields.IntegerField(
 47 #         min_value=10,
 48 #         max_value=1000,
 49 #         error_messages={
 50 #             'required': 't2不能为空',
 51 #             'invalid': 't2格式错误,必须是数字',
 52 #             'min_value': '必须大于10',
 53 #             'max_value': '必须小于1000',
 54 #         },
 55 #     )
 56 #     t3 = fields.EmailField(
 57 #         error_messages={
 58 #             'required': 't3不能为空',
 59 #             'invalid': 't3格式错误,必须是邮箱格式',
 60 #         }
 61 #     )
 62 
 63 
 64 
 65 
 66 
 67 class TestForm(Form):
 68     t1 = fields.CharField(required=True,max_length=8,min_length=2,
 69         error_messages={
 70             'required': '不能为空',
 71             'max_length': '太长',
 72             'min_length': '太短',
 73         }
 74     )
 75     t2 = fields.EmailField()
 76 
 77 def test(request):
 78     if request.method == "GET":
 79         obj = TestForm()
 80         return render(request,'test.html',{'obj': obj})
 81     else:
 82         obj = TestForm(request.POST)
 83         if obj.is_valid():
 84             print(obj.cleaned_data)
 85         else:
 86             print(obj.errors)
 87         return render(request,'test.html',{'obj':obj})
 88 
 89 
 90 
 91 class RegiterForm(Form):
 92     user = fields.CharField(min_length=8)
 93     email = fields.EmailField()
 94     password = fields.CharField()
 95     phone = fields.RegexField('139\d+')
 96 
 97 
 98 def register(request):
 99     if request.method == 'GET':
100         obj = RegiterForm()
101         return render(request,'register.html',{'obj':obj})
102     else:
103         obj = RegiterForm(request.POST)
104         if obj.is_valid():
105             print(obj.cleaned_data)
106         else:
107             print(obj.errors)
108         return render(request,'register.html',{'obj':obj})
views.py
 1 """s4day77 URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from django.contrib import admin
18 from app01 import views
19 urlpatterns = [
20     url(r'^admin/', admin.site.urls),
21     url(r'^login/', views.login),
22     url(r'^ajax_login/', views.ajax_login),
23     url(r'^test/', views.test),
24     url(r'^register/', views.register),
25 ]
urls.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <h1>用户登录</h1>
 9     <form id="f1" action="/login/" method="POST">
10         {% csrf_token %}
11         <p>
12             <input type="text" name="user" />{{ obj.errors.user.0 }}
13         </p>
14         <p>
15             <input type="password" name="pwd" />{{ obj.errors.pwd.0 }}
16         </p>
17         <input type="submit" value="提交" />
18         <a onclick="submitForm();">提交</a>
19     </form>
20     <script src="/static/jquery-1.12.4.js"></script>
21     <script>
22         function submitForm(){
23             $('.c1').remove();
24             $.ajax({
25                 url: '/ajax_login/',
26                 type: 'POST',
27                 data: $('#f1').serialize(),// user=alex&pwd=456&csrftoen=dfdf\
28                 dataType:"JSON",
29                 success:function(arg){
30                     console.log(arg);
31                     if(arg.status){
32 
33                     }else{
34                         $.each(arg.msg,function(index,value){
35                             console.log(index,value);
36                             var tag = document.createElement('span');
37                             tag.innerHTML = value[0];
38                             tag.className = 'c1';
39                             $('#f1').find('input[name="'+ index +'"]').after(tag);
40                         })
41                     }
42                 }
43             })
44         }
45     </script>
46 </body>
47 </html>
login.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8     <form action="/test/" method="POST" novalidate>
 9         {% csrf_token %}
10         <p>
11             {{ obj.t1 }}{{ obj.errors.t1.0 }}
12         </p>
13         <p>
14             {{ obj.t2 }}{{ obj.errors.t2.0 }}
15         </p>
16         <input type="submit" value="提交" />
17     </form>
18 </body>
19 </html>
test.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title></title>
 6 </head>
 7 <body>
 8 
 9     <form action="/register/" method="POST" novalidate>
10         {% csrf_token %}
11         <p>
12             {{ obj.user }} {{ obj.errors.user.0 }}
13         </p>
14         <p>
15             {{ obj.email }} {{ obj.errors.email.0 }}
16         </p>
17         <p>
18             {{ obj.password }} {{ obj.errors.password.0 }}
19         </p>
20         <p>
21             {{ obj.phone }} {{ obj.errors.phone.0 }}
22         </p>
23         <input type="submit" value="提交"  />
24     </form>
25 </body>
26 </html>
register.html

总结:

1
2
- Ajax,仅用验证功能
- Form,验证功能,生成HTML标签

Form类的字段和插件

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

1、Django内置字段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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    pub_date=forms.DateField(widget=widgets.TextInput(attrs={"type":"date"}))
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
  
DurationField(Field)            时间间隔:%%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,                  # 查询数据库中的数据  publish=forms.ModelChoiceField(queryset=Publish.objects.all())
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
      
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
                                          authors=forms.ModelMultipleChoiceField(queryset=Author.objects.all())
  
      
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类型
    ...

注:UUID是根据MAC以及当前时间等创建的不重复的随机字符串

 1 >>> import uuid
 2 
 3     # make a UUID based on the host ID and current time
 4     >>> uuid.uuid1()    # doctest: +SKIP
 5     UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')
 6 
 7     # make a UUID using an MD5 hash of a namespace UUID and a name
 8     >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
 9     UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
10 
11     # make a random UUID
12     >>> uuid.uuid4()    # doctest: +SKIP
13     UUID('16fd2706-8baf-433b-82eb-8c7fada847da')
14 
15     # make a UUID using a SHA-1 hash of a namespace UUID and a name
16     >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
17     UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
18 
19     # make a UUID from a string of hex digits (braces and hyphens ignored)
20     >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')
21 
22     # convert a UUID to a string of hex digits in standard form
23     >>> str(x)
24     '00010203-0405-0607-0809-0a0b0c0d0e0f'
25 
26     # get the raw 16 bytes of the UUID
27     >>> x.bytes
28     b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'
29 
30     # make a UUID from a 16-byte string
31     >>> uuid.UUID(bytes=x.bytes)
32     UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
View Code

2、Django内置插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

常用选择插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 单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
# )

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。

 方式一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
  
class MyForm(Form):
  
    user = fields.ChoiceField(
        # choices=((1, '上海'), (2, '北京'),),
        initial=2,
        widget=widgets.Select
    )
  
    def __init__(self*args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
        # 或
        self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')

方式二:

使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现

1
2
3
4
5
6
7
8
9
10
from django import forms
from django.forms import fields
from django.forms import widgets
from django.forms import models as form_model
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
  
class FInfo(forms.Form):
    authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())
    # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())zidi

自定义验证规则

方式一:

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

方式二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError
  
  
# 自定义验证规则
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手机号码格式错误')
  
  
class PublishForm(Form):
  
  
    title = fields.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required''标题不能为空',
                                            'min_length''标题最少为5个字符',
                                            'max_length''标题最多为20个字符'},
                            widget=widgets.TextInput(attrs={'class'"form-control",
                                                          'placeholder''标题5-20个字符'}))
  
  
    # 使用自定义验证规则
    phone = fields.CharField(validators=[mobile_validate, ],
                            error_messages={'required''手机不能为空'},
                            widget=widgets.TextInput(attrs={'class'"form-control",
                                                          'placeholder': u'手机号码'}))
  
    email = fields.EmailField(required=False,
                            error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
                            widget=widgets.TextInput(attrs={'class'"form-control"'placeholder': u'邮箱'}))

方法三:自定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from django import forms
    from django.forms import fields
    from django.forms import widgets
    from django.core.exceptions import ValidationError
    from django.core.validators import RegexValidator
  
    class FInfo(forms.Form):
        username = fields.CharField(max_length=5,
                                    validators=[RegexValidator(r'^[0-9]+$''Enter a valid extension.''invalid')], )
        email = fields.EmailField()
  
        def clean_username(self):
            """
            Form中字段中定义的格式匹配完之后,执行此方法进行验证
            :return:
            """
            value = self.cleaned_data['username']
            if "666" in value:
                raise ValidationError('666已经被玩烂了...''invalid')
            return value

方式四:同时生成多个标签进行验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from django.forms import Form
from django.forms import widgets
from django.forms import fields
  
from django.core.validators import RegexValidator
  
  
############## 自定义字段 ##############
class PhoneField(fields.MultiValueField):
    def __init__(self*args, **kwargs):
        # Define one message for all fields.
        error_messages = {
            'incomplete''Enter a country calling code and a phone number.',
        }
        # Or define a different message for each field.
        = (
            fields.CharField(
                error_messages={'incomplete''Enter a country calling code.'},
                validators=[
                    RegexValidator(r'^[0-9]+$''Enter a valid country calling code.'),
                ],
            ),
            fields.CharField(
                error_messages={'incomplete''Enter a phone number.'},
                validators=[RegexValidator(r'^[0-9]+$''Enter a valid phone number.')],
            ),
            fields.CharField(
                validators=[RegexValidator(r'^[0-9]+$''Enter a valid extension.')],
                required=False,
            ),
        )
        super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False*args,
                                         **kwargs)
  
    def compress(self, data_list):
        """
        当用户验证都通过后,该值返回给用户
        :param data_list:
        :return:
        """
        return data_list
  
############## 自定义插件 ##############
class SplitPhoneWidget(widgets.MultiWidget):
    def __init__(self):
        ws = (
            widgets.TextInput(),
            widgets.TextInput(),
            widgets.TextInput(),
        )
        super(SplitPhoneWidget, self).__init__(ws)
  
    def decompress(self, value):
        """
        处理初始值,当初始值initial不是列表时,调用该方法
        :param value:
        :return:
        """
        if value:
            return value.split(',')
        return [NoneNoneNone]

初始化数据

在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。

1、Form

from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
  
  
class MyForm(Form):
    user = fields.CharField()
  
    city = fields.ChoiceField(
        choices=((1, '上海'), (2, '北京'),),
        widget=widgets.Select
    )
View Code

2、Views

from django.shortcuts import render, redirect
from .forms import MyForm
  
  
def index(request):
    if request.method == "GET":
        values = {'user': 'root', 'city': 2}
        obj = MyForm(values)
  
        return render(request, 'index.html', {'form': obj})
    elif request.method == "POST":
        return redirect('http://www.google.com')
    else:
        return redirect('http://www.google.com')
View Code

3、HTML

<form method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    <p>{{ form.user }} {{ form.user.errors }}</p>
    <p>{{ form.city }} {{ form.city.errors }}</p>
  
    <input type="submit"/>
</form>
View Code

关于对字段认证[clean,clean_字段]的补充(关于错误信息)

 1 class RegesterForm(Form):
 2     username = fields.CharField(
 3         widget=widgets.TextInput(
 4             attrs={"class": "form-control",
 5                    "placeholder": "用户名",
 6                    }),
 7         error_messages={
 8             "required": "内容不能为空",
 9             "invalid": "格式错误,请重新输入!", })
10  
11     nickname = fields.CharField(
12         widget=widgets.TextInput(
13             attrs={"class": "form-control",
14                    "placeholder": "即昵称",
15                    }),
16         error_messages={
17             "required": "内容不能为空",
18             "invalid": "格式错误,请重新输入!", })
19  
20     email = fields.EmailField(
21         widget=widgets.TextInput(
22           attrs={
23               "class": "form-control",
24               "placeholder": "请输入您查用邮箱",
25           }),
26         error_messages = {
27             "required": "内容不能为空",
28             "invalid": "格式错误,请重新输入!",}
29     )
30  
31     password = fields.CharField(
32         widget=widgets.PasswordInput(
33             attrs={"class": "form-control",
34                    "placeholder": "密码,必须包含数字,字母!",
35                    }),
36         error_messages={
37             "required": "内容不能为空",
38             "invalid": "格式错误,请重新输入!", })
39  
40     passwords = fields.CharField(
41         widget=widgets.PasswordInput(
42             attrs={"class": "form-control",
43                    "placeholder": "请输入确认密码",
44                    }),
45         error_messages={
46             "required": "内容不能为空",
47             "invalid": "格式错误,请重新输入!", })
48  
49     avatar = fields.FileField(
50         widget=widgets.FileInput(
51             attrs={'id':"imgSelect",}),
52     )
53     code = fields.CharField(
54         widget=widgets.TextInput(
55             attrs={
56                 "class": "form-control",
57                 "placeholder": "验证码",
58             },)
59     )
60     def __init__(self,request,*args,**kwargs):
61         super(RegesterForm,self).__init__(*args,**kwargs)
62         self.request = request
63  
64     def clean_code(self):
65         input_code = self.cleaned_data["code"]
66         session_code = self.request.session.get("code")
67         if input_code.upper() == session_code.upper():
68             return input_code
69         raise ValidationError("验证码错误,请重新输入!")
70     #自定义字段验证方法,获取错误信息的方式不变,还是   对象.errors.字段名.0
71  
72     def clean(self):
73         p1 = self.cleaned_data.get("password")
74         p2 = self.cleaned_data.get("passwords")
75         if p1 == p2:
76             return self.cleaned_data
77         else:
78             self.add_error("passwords",ValidationError("输入的密码不一致!"))
View Code
 1 对于Form组件的错误信息:
 2     注意再注意:
 3     默认错误信息方式: raise ValidationError("输入的密码不一致!")
 4     自定义对已拿到所有字段数据进行验证的时候,这种方式在获取错误信息时就发生改变,查看源码发现如果有错误的话,他会执行self.add_error(None, e)
 5     通过方法给字段None添加错误信息,查看add_error的源码以及执行过程,发现None = NON_FIELD_ERRORS。也就是说会默认给__all__字段或
 6     NON_FIELD_ERRORS写上错误信息。原因:内部定义的NON_FIELD_ERRORS="__all__",
 7     获取方式:
 8         前端获取 是通过obj.non_field_errors
 9         后台获取是通过 obj.errors["__all__"]  or   obj.errors[NON_FIELD_ERRORS]
10  
11     我们知道,当报错的时候,内部会自动去添加错误信息,若是我们能否手动指定某个字段呢?答案是肯定的。
12     这样我们自己添加异常的错误信息,就能直接通过{{obj.errors.passwords.0}}获取到与其他的无疑。
13     语法:
14         self.add_error('字段名称', 错误异常对象)
总结

以上代码是我写用户注册时的form组件,由于牵扯到对输入字段的认证问题!如果选择在视图函数中,那代码的重复率可就老了去了!!!所以就使用了Form组件中的字段认证的方法!!!

前面已经介绍过Form组件中两种验证方式的函数,一个对字段的 clean_字段;另一个是等所有字段验证之后,对所有验证过的clean_data进行更高的验证:clean;今天我要说的是关于验证过程中,产生和收集错误信息的问题。详见上面代码!!!

-------------------------------------------------------------------------------------bottom another------------------------------------------------------------------------------------------

一、Form组件:

django框架提供了一个form类,来处理web开发中的表单相关事项。众所周知,form最常做的是对用户输入的内容进行验证,为此django的forms类提供了全面的内容验证和保留用户上次输入数据的支持。

 form组件有2大大功能

对用户提交的内容进行验证(from表单/Ajax)

保留用户上次输入的内容

 

1、对用户提交的数据进行验证

form组件验证的原理

 

1.obj=Form()form组件类实例化时找到类中所有的字段 把这些字段 变成组合成字典;

self.fields={‘user’:正则表达式1,‘pwd’:正则表达式2}

2.循环self.fields字典(自己写的字段)

for  k,v  in self.fields.items():

    K是user,pwd

   v是正则表达式

3.每次循环通过self.fields字典的键, 一个一个的去get前端POST提交的数据 得到用户输入数据;

input_value= request.post.get(‘k’)(所以form字段的名称,要和前端的name属性匹配)

4.每次拿到用户输入的数据 (input_value)和进行正则表达式匹配;

5.匹配成功flag=True 匹配失败flag=falsh,最后 return flag  obj.is_valid=flag。

如果For自带的规则和正则满足不了验证需求,可在Form类中自定义方法,做扩展。

6.每个字段验证通过后,每个字段执执行self.clean_filelds函数(自定义 对Form类中的字段做单独验证,比如去数据库查询判断一下用户提交的数据是否存在?)

7. 执行Form组件的clean_form方法进行整体验证!(既然每个字段都验证了,就可以对用户提交的数据做整体验证了!比如进行联合唯一的验证)

 8.最后执行类似 clean_form的post_clean方法结束验证。(一般不使用post_clean做自定义过滤,clean_form方法完全可以解决)

form表单提交验证(form表单(会发起 get)提交刷新失去上次内容)

 1 from django.shortcuts import render,HttpResponse,redirect
 2 from django.forms import Form
 3 from django.forms import fields
 4 
 5 class Login(Form):
 6                               #from验证规则 用户名 6-10字符  required不能为空
 7     name=fields.CharField(max_length=10,
 8                           min_length=6,
 9                           required=True,
10                            error_messages={
11                                'required':'用户名不能为空',  #error_messages参数 自定义错误信息
12                                'min_length':'太短了',
13                                 'max_length': "太长了",
14                                            }
15 
16                           )
17                                                                     # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
18     pwd= fields.CharField(min_length=3,
19                           required=True,
20                           error_messages={
21                               'required': '密码不能为空',  # error_messages参数 自定义错误信息
22                               'min_length': '太短了',
23                               'max_length': "太长了",
24                           }
25 
26 
27 
28                           )
29 
30 
31 def index(request):
32     if request.method=='GET':
33         return render(request,'login.html')
34     else:
35         obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一起
36         res=obj.is_valid()         #自动校验  给出结果 True 或者 False
37         if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
38             print(obj.cleaned_data)
39             return redirect('http://www.baidu.com')                 #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
40         else:
41            return  render(request,'login.html',{'obj':obj})
View Code

Ajax提交验证(不会刷新,上次输入内容自动保留)

Django的form验证功能不仅限于对form表单提交的数据验证,同样适用于ajax提交方式

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>ajx提交</title>
 6 </head>
 7 <body>
 8 <form method="post" action="/aja_login/" id="f1">
 9   {%csrf_token%}
10 <p>用户:<input type="text" name="name"></p>
11 <p>密码:<input type="password" name="pwd"></p>
12 <p><input type="button" onclick="Ajxform()" value="aja提交"></p>
13 </form>
14 </body>
15 <script src="/static/zhanggen.js"></script>
16 <script> function Ajxform(){
17     $('.c1').remove()
18     $.ajax({
19         url:'/alogin/',
20         type:'POST',
21         dataType:'JSON',
22         data:$('#f1').serialize(),
23         success:function (args) {
24             if (args.status){ }
25             else{
26 {#                {status: false, msg: Object}#}
27 {#                console.log(args);#}
28 {#                Jquery循环服务端 传过来的 错误信息对象#}
29                 $.each(args.msg,function (index,value) {
30                     console.log(index,value);
31 {#                 index----> name ["太短了"]#}
32 {#                 value-----pwd["密码不能为空"]#}
33                     var tag=document.createElement('span');
34                     tag.innerHTML= value[0];
35                     tag.className='c1';
36                     console.log(index);
37 {#                    寻找input下 属性为 name 和pwd的标签(字符串拼接) 在他们后半加 上tag标签也就是错误 信息 #}
38                     $('#f1').find('input[name="'+ index +'"]').after(tag)
39 
40                 })
41             }
42 
43                }})}
44 </script>
45 </html>
View Code

Views

 1 from django.shortcuts import render,HttpResponse,redirect
 2 from django.forms import Form
 3 from django.forms import fields
 4 import json
 5 class Login(Form):
 6                               #from验证规则 用户名 6-10字符  required不能为空
 7     name=fields.CharField(max_length=10,
 8                           min_length=6,
 9                           required=True,
10                          error_messages={
11                                'required':'用户名不能为空',  #error_messages参数 自定义错误信息
12                                'min_length':'太短了',
13                                 'max_length': "太长了",
14                                            }
15 
16                           )
17                                                                     # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
18     pwd= fields.CharField(min_length=3,
19                           required=True,
20                           error_messages={
21                               'required': '密码不能为空',  # error_messages参数 自定义错误信息
22                               'min_length': '太短了',
23                               'max_length': "太长了",})
24 
25 
26 
27 def agx_login(request):
28     ret={'status':True,'msg':None}
29     if request.method=='GET':
30        return render(request,'ajalogin.html')
31     else:
32         obj=Login(request.POST)
33         ret['status']=False
34         ret['msg']=obj.errors
35         return HttpResponse(json.dumps(ret))
View Code

自定义正则表达式验证

密码修改实例

password_complexity='^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).+$' #密码复杂性要求:密码必须包含数字、大、写字母
 1 {% extends "arya/layout.html" %}
 2 {% block  out_css %}
 3     <link rel="stylesheet" href="/arya/static/arya/css/form-control.css"/>
 4 {% endblock %}
 5 
 6 {% block content %}
 7     {% csrf_token %}
 8     <div>
 9         <div class="row">
10             <a class="btn btn-default" href="{{ request.META.HTTP_REFERER }}">返回</a>
11             <div class="col-md-5 col-md-offset-3">
12                 <form>
13                     <div class="form-group">
14                         <label for="exampleInputPassword1">请输入原始密码</label>
15                         <input name="old_pwd" type="password" class="form-control" id="exampleInputPassword0"
16                                placeholder="原始密码">
17                     </div>
18                     <div class="form-group">
19                         <label for="exampleInputPassword1">新的密码</label>
20                         <input name="first_new_pwd" type="password" class="form-control" id="exampleInputPassword1"
21                                placeholder="新的密码">
22                     </div>
23                     <div class="form-group">
24                         <label for="exampleInputPassword1">再次输入新密码</label>
25                         <input name="second_new_pwd" type="password" class="form-control" id="exampleInputPassword2"
26                                placeholder="再次输入新密码">
27                     </div>
28 
29                     <button id="submit_pwd" type="button" class="btn btn-default">提交</button>
30 
31                 </form>
32                 <br>
33                 <p style="color: red" id="__all__" class="error_msg"></p>
34                 <br>
35                 <p>设置密码时请符合以下规则:最小长度8、包含大小写英文字母、数字。</p>
36             </div>
37         </div>
38     </div>
39     {#    </section>#}
40     <script>
41         $('#submit_pwd').click(function () {
42             var $csrf = $("[name='csrfmiddlewaretoken']").val();
43             var $old_pwd = $('[name="old_pwd"]').val();
44             var $first_new_pwd = $('[name="first_new_pwd"]').val();
45             var $second_new_pwd = $('[name="second_new_pwd"]').val();
46 
47             var pwd_formdata = new FormData();
48             pwd_formdata.append('csrfmiddlewaretoken', $csrf);
49             pwd_formdata.append('old_pwd', $old_pwd);
50             pwd_formdata.append('first_new_pwd', $first_new_pwd);
51             pwd_formdata.append('second_new_pwd', $second_new_pwd);
52 
53 
54             $.ajax({
55                 type: 'post',
56                 data: pwd_formdata,
57                 processData: false,
58                 contentType: false,
59                 success: function (data) {
60                     var response = JSON.parse(data);
61                     var $error_p = $('<p class="error_msg" style="color:red">'+ '</p>');
62 
63                     if (response.status == 200) {
64                         window.location = "/login/"
65                     }
66 
67                     if (response.status == 404) {
68                         var error_p =$error_p.text(response.msg.old_pwd);
69                         $('[name="old_pwd"]').after(error_p)
70                     }
71                     else {
72                         $.each(response.msg, function (k, v) {
73                             var $input_tag = $("[name=" + k +"]");
74                             var error_p = '<p style="color: red" class="error_msg">'+v[0]+'</p>';
75                             $input_tag.after(error_p);
76                             console.log(response.msg);
77                             if (k == '__all__') {
78                                 $('#__all__').text(v[0])
79                             }
80                         });
81                     }
82                     setTimeout("$('.error_msg').remove()", 3000);
83                 }
84             })
85         })
86     </script>
87 {% endblock %}
前端模板
 1 def changepwd(request):  #修改密码视图
 2     if request.method=='POST':
 3         current_user = request.session.get('username')
 4         response_info = {'status':200,'msg':None}
 5         user_db_obj = models.UserInfo.objects.filter(username=current_user).first()
 6         if not user_db_obj or user_db_obj.password != make_md5(request.POST.get('old_pwd').strip()):
 7             response_info['status'] = 404
 8             response_info['msg'] = {'old_pwd': '原始密码错误.'}
 9         else:
10             obj = FormCheck.Set_password(request.POST)
11             if not obj.is_valid():
12                 response_info['status'] = 403
13                 response_info['msg']=obj.errors
14             else:
15                 user_db_obj.password = make_md5(request.POST.get('second_new_pwd').strip())
16                 user_db_obj.save()
17         return HttpResponse(json.dumps(response_info, ensure_ascii=False))
18     return render(request,'woke_order/changepwd.html')
Django视图
 1 from django.forms import Form
 2 from django.forms import fields
 3 from django.forms import widgets
 4 from cmdb.models import *
 5 import re
 6 from django.core.exceptions import ValidationError
 7 
 8 class Set_password(Form):
 9     password_complexity='^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).+$'
10 
11     old_pwd=fields.RegexField(password_complexity,
12         required=True,
13         min_length=8,
14         error_messages={'invalid':'不满足密码复杂性要求','required':'密码不能为空!','min_length':'密码长度少于8位'},
15     )
16 
17     first_new_pwd=fields.RegexField(password_complexity,
18         required=True,
19         min_length=8,
20         error_messages={'invalid':'不满足密码复杂性要求','required':'密码不能为空!','min_length':'密码长度少于8位'},
21     )
22 
23     second_new_pwd =fields.RegexField(password_complexity,
24         required=True,
25         min_length=8,
26         error_messages={'invalid':'不满足密码复杂性要求','required':'密码不能为空!','min_length':'密码长度少于8位'},
27     )
28 
29     def clean(self):
30         first_new_pwd=self.cleaned_data.get('first_new_pwd')
31         second_new_pwd=self.cleaned_data.get('second_new_pwd')
32         if first_new_pwd and second_new_pwd:
33             if first_new_pwd.strip() == second_new_pwd.strip():
34                 return self.cleaned_data
35         raise ValidationError("两次密码不一致")
Django的form验证

IP 和端口

ipaddr_validate="^((?:(2[0-4]\d)|(25[0-5])|([01]?\d\d?))\.){3}(?:(2[0-4]\d)|(255[0-5])|([01]?\d\d?))$"
port_validate='^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$' 
 1 from django.forms import Form,fields
 2 from django.forms import widgets,forms
 3 import re
 4 ipaddr_validate="^((?:(2[0-4]\d)|(25[0-5])|([01]?\d\d?))\.){3}(?:(2[0-4]\d)|(255[0-5])|([01]?\d\d?))$"
 5 port_validate='^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$'
 6 class dbinfo_create(Form):
 7     data_mode_type=fields.CharField(required=True,error_messages={'required':'数据库模型不能为空.'})
 8     database_type=fields.CharField(required=True,error_messages={'required':'数据库类型不能为空'})
 9     host=fields.RegexField(ipaddr_validate,required=True,error_messages={'required':'IP不能为空','invalid':'不合法的IP地址'})
10     port=fields.RegexField(port_validate,required=True,error_messages={'required':'端口不能为空.','invalid':'端口无效'})
11     # instance_nikename=fields.CharField(max_length=20,error_messages={'required':'端口不能为空.',"max_length":"标题不能超过20个字"})
12     db_business=fields.CharField(required=True,error_messages={'required':'请说明所属业务线'})
13     DBA=fields.CharField(required=True,error_messages={'required':'请说明DBA'})
14     responsible_person=fields.CharField(required=True, error_messages={'required':'请选择相关责任人!'})
验证

2、动态生成HTML标签,保留用户上次输入的内容。

如何保留用户上次输入的内容?

由于form表单submit之后(发送post请求) 数据提交到 后端,不管前端输入的数据是否正确,服务端也要响应,所以页面会刷新;

所以无法保留用户上次输入的内容;如何解决呢?

1、把定义的定义的Form类,实例化(obj=Login() )内部调用一个__str__的方法,如果没有传值 返回<input type="text" name=“字段”>name='字段名空的input标签

2、把这个实例化之后的对象传到前端显示,让用户输入值;用户输入值通过post方法提交到后台。

3、如果后台实例化一个对象 obj=Login(request.POST)传入了值, <input type="text" name=“字段” value='request.post的数据'>然后后端再返回客户端就可以看到用户输入的值了!

保留用户上次输入的内容 是利用了 obj=Login(request.POST)接收了用户输入的值

视图

 1 from django import  forms
 2 
 3 class Myform(forms.Form):            #1、写1个继承forms.Form的类,定制form表单的数据类型;
 4     user=forms.CharField(max_length=32,min_length=3,label='用户名',
 5                          error_messages={'required':'不能为空'},
 6                          widget=forms.TextInput(attrs={'class':'sb','placeholder':'用户名'},)
 7                          )
 8     age=forms.IntegerField(label='年龄',error_messages={'required':'不能为空'},)
 9     email=forms.EmailField(label='邮箱',error_messages={'required':'不能为空'},)
10 
11 def register2(request):
12     if request.method=='GET':
13         forms_obj=Myform()               #2、实例化类,把对象渲染到模板
14         return render(request,'form——register.html',{'forms_obj':forms_obj})
15     else:
16         forms_obj=Myform(request.POST)   #3、把提交的数据封装成form对象
17         if forms_obj.is_valid():         #4、使用 form对象的.is_valid()方法,校验提交过来的数据是否符合验证规则
18             data=forms_obj.cleaned_data  #5、获取验证通过的数据(字典类型,可直接 **dict插入数据库)
19             # User.objects.create_user(**data)
20             return HttpResponse('OK')
21         else:
22             print(forms_obj.cleaned_data) #6、由于用户在form表单提交了值,利用这一点,
23                                             # 把forms_obj=Myform(request.POST)渲染到前端就可以保存用户输入的值
24         return render(request, 'form——register.html', {'forms_obj':forms_obj})
View Code

前台

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django_form验证</title>
</head>
<body>
<form action="/form/" method="post" novalidate>
    {% csrf_token %}
    <p>用户:{{ forms_obj.user }}{{ forms_obj.errors.user.0 }}</p>
      <p>年龄:{{ forms_obj.age }}{{ forms_obj.errors.age.0}}</p>
    <p>邮箱:{{ forms_obj.email }}{{ forms_obj.errors.email.0}}</p>
    <button>提交</button>
</form>
</body>
</html>
View Code
 1 from django.shortcuts import render,HttpResponse,redirect
 2 from django.forms import Form
 3 from django.forms import fields
 4 import json
 5 class Login(Form):
 6                               #from验证规则 用户名 6-10字符  required不能为空
 7     name=fields.CharField(max_length=10,
 8                           min_length=6,
 9                           required=True,
10                          error_messages={
11                                'required':'用户名不能为空',  #error_messages参数 自定义错误信息
12                                'min_length':'太短了',
13                                 'max_length': "太长了",
14                                            }
15 
16                           )
17                                                                     # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
18     pwd= fields.CharField(min_length=3,
19                           required=True,
20                           error_messages={
21                               'required': '密码不能为空',  # error_messages参数 自定义错误信息
22                               'min_length': '太短了',
23                               'max_length': "太长了",})
24 
25 
26 def index(request):
27     ret={'status':True,'msg':None}
28     if request.method=='GET':
29         obj=Login()                 #自动生成空白的input标签 发送给客户端)
30         return render(request,'login.html',{'obj':obj})
31     else:
32         obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一
33         res=obj.is_valid()         #自动生成空白的input标签 发送
34         if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
35             return HttpResponse('OK') #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
36         else:
37            return render(request,'login.html',{'obj':obj})
View Code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<form method="post" action="/login/" id="f1" novalidate >
     {%csrf_token%}
       <h1>用户登录</h1>
        <p>用户名 {{obj.name}}{{ obj.name.errors.0}}</p>
        <p>密码:{{ obj.pwd}}{{ obj.pwd.errors.0}}</p>
        <p><input type="submit" value="登录"></p>
</form>
</body>
</html>
View Code

 3、承上启下 form组件的套路(执行流程):

(1)在后端定义类和字段,实例化Form类;

(2)到用户 发送get请求时,服务端渲染到模板(空标签/默认值)发送到客户端显示

(3)客户端填数据,POST提交到后端;

(4)后端验证,返回结果给前端;(切记Form组件是在后端生成,发送给客户端显示,客户端填完数据在发回服务端!)

二、form组件使用:

1、导入Form插件

from django.forms import Form,fields
from django.forms import widgets
 1 class Class_form(Form):
 2     title=fields.RegexField('全栈\d+',
 3                             # initial='全栈', #设置input标签中的默认值
 4                             min_length=2,
 5                             required=True,
 6                             error_messages={'invalid':"必须以全栈开头",
 7                                             'min_length':'太短了',
 8                                             'required':"不能为空",
 9                                             }
10                             )
11 class Students(Form):
12     name=fields.CharField(required=True,
13     widget=widgets.TextInput(attrs={'class':'form-control'}),
14     error_messages={'required':'姓名不能为空'})
15     sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
16     widget = widgets.TextInput(attrs={'class': 'form-control'})
17 
18                          )
19     cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
20     attrs = {'class': 'form-control'}
21 
22                                                      ),
23 
24                                 )
25 class teacher_form(Form):
26     tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
27    #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
28    #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
29    #      attrs={'multiple':'multiple'})
30    #                           )
31    #  classes=fields.CharField(widget=widgets.SelectMultiple(
32    # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
33    #   choices= models.Classes.objects.values_list('id','title')) )
34     classes=fields.MultipleChoiceField(
35         choices=models.Classes.objects.values_list('id','title'),
36         widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
37     def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
38         super(teacher_form,self).__init__(*args,**kwargs)
39         self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title')
40 
41 
42 
43 class Test_form(Form):
44     name=fields.CharField()  #动态生成 text类型的input标签
45     text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
46     age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
47     holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
48         choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
49         widget=widgets.CheckboxSelectMultiple)
50     sex=fields.MultipleChoiceField(
51         choices=[(1,''),(2,'')],
52         widget=widgets.RadioSelect
53     )
54 
55     select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
56                                                                    # 通过form组件的MultipleChoiceField字段
57     mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
58                                  widget=widgets.SelectMultiple)
59     file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code
2、定义类和字段(验证规则) 扩展方法

class Form_login(Form):

                 字段                               参数

  user=fields.RegexField正则表达式,验证规则,error_messages={错误信息},widget=html标签插件attrs = {'标签插件的属性'})

 

三、form的钩子函数

Django的form在obj.is_valid()方法内提供2个钩子函数,以便我们随时调用他自定制一些复杂的验证规则;

 局部钩子函数 

 1 class Class_form(Form):
 2     title=fields.RegexField('全栈\d+',
 3                             # initial='全栈', #设置input标签中的默认值
 4                             min_length=2,
 5                             required=True,
 6                             error_messages={'invalid':"必须以全栈开头",
 7                                             'min_length':'太短了',
 8                                             'required':"不能为空",
 9                                             }
10                             )
11 class Students(Form):
12     name=fields.CharField(required=True,
13     widget=widgets.TextInput(attrs={'class':'form-control'}),
14     error_messages={'required':'姓名不能为空'})
15     sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
16     widget = widgets.TextInput(attrs={'class': 'form-control'})
17 
18                          )
19     cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
20     attrs = {'class': 'form-control'}
21 
22                                                      ),
23 
24                                 )
25 class teacher_form(Form):
26     tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
27    #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
28    #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
29    #      attrs={'multiple':'multiple'})
30    #                           )
31    #  classes=fields.CharField(widget=widgets.SelectMultiple(
32    # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
33    #   choices= models.Classes.objects.values_list('id','title')) )
34     classes=fields.MultipleChoiceField(
35         choices=models.Classes.objects.values_list('id','title'),
36         widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
37     def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
38         super(teacher_form,self).__init__(*args,**kwargs)
39         self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title')
40 
41 
42 
43 class Test_form(Form):
44     name=fields.CharField()  #动态生成 text类型的input标签
45     text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
46     age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
47     holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
48         choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
49         widget=widgets.CheckboxSelectMultiple)
50     sex=fields.MultipleChoiceField(
51         choices=[(1,''),(2,'')],
52         widget=widgets.RadioSelect
53     )
54 
55     select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
56                                                                    # 通过form组件的MultipleChoiceField字段
57     mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
58                                  widget=widgets.SelectMultiple)
59     file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code

 全局钩子函数

如果要想要同时对2个form字段进行验证,就需要全局钩子函数(应用 验证2次输入的密码是否一致),可以调用他们自定制复杂的form验证规则,

问题1:  注册页面输入为空,报错:keyError:找不到password

def clean(self):
        print("---",self.cleaned_data)
        #  if self.cleaned_data["password"]==self.cleaned_data["repeat_password"]:        
        #  报错原因:self.cleaned_data是干净数据,如果页面没有输入内容,则self.cleaned_data没有password。
        改如下:
        if self.cleaned_data.get("password")==self.cleaned_data.get("repeat_password"):
            return self.cleaned_data
        else:
            raise ValidationError("两次密码不一致")
View Code

  

四、渲染到模板

1.简单粗暴型

注意:模板语言{{form_obj.as_p}},一定要在设置lable参数

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名') 
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(choices=[(1,"技术部"),(2,'销售部'),(3,'市场部'),],label='部门')
{{obj.as_p}}以P标签的形式全部显示

<table> 注意加table标签形式全部显示
{{obj.as_table}}
</table>


<ul>注意加ul标签形式全部显示
{{obj.as_table}}
</ul>
View Code

2.灵活定制型

<p>姓名:{{ obj.name }}</p>
<p>性别:{{ obj.sex }}</p>
<p>爱好: {{ obj.holby }}</p>
<p>婚姻状况:{{ obj.age }}</p>
<p>个人简介 {{ obj.text }}</p>
<p>工作地点: {{ obj.select }}</p>
<p>居住地点:{{ obj.mselect }}</p>
<p>资料上传:{{obj.file}}</p>
View Code

 

五、页面显示用户填完数据提交回来后台验证

数据校验

obj=classForm_login(request.POST )

默认校验:obj=classForm_login(data={} ) 含有错误信息: obj=Class_form(initial={'title':class_obj.title})只有html标签

obj.is_valid()  获取校验结果,验证通过返回True,失败则返回False

obj.errors获取错误信息

obj.cleand_data 获取正确的数据

 

六、基于Form组件的学生管理系统(项目)

路由

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^classes/',views.classes),
    url(r'^classes_add(\d+)/',views.classes_add),
    url(r'^classes_edit(\d+)/',views.classes_edit),
    url(r'^students/',views.students),
    url(r'^students_add(\d+)/',views.students_add),
    url(r'^students.edit(\d+)/',views.students_edit),
    url(r'^teachers/',views.teacher),
    url(r'^teacher_add/',views.teacher_add),
    url(r'^teacher_edit(\d+)/',views.teacher_edit),
    url(r'^test/',views.test),
]
View Code

视图

  1 def classes(request):
  2     if request.method=='GET':
  3         c_list=models.Classes.objects.all()
  4         return render(request,'class_list.html',{'clist':c_list})
  5 
  6 def classes_add(request,args):
  7     if request.method=='GET':
  8         obj=Class_form()
  9         return render(request,'class_add.html',{'obj':obj})
 10     else:
 11         obj=Class_form(request.POST)
 12         if obj.is_valid():
 13             models.Classes.objects.create(**obj.cleaned_data )
 14             return redirect('/classes/')
 15         else:
 16             print('NI')
 17             return render(request,'class_add.html',{'obj':obj})
 18 
 19 def classes_edit(request,args):
 20     if request.method=='GET':
 21         class_obj=models.Classes.objects.filter(id=args).first()
 22         obj=Class_form(initial={'title':class_obj.title})
 23         return render(request,'class_edit.html',{'obj':obj,'nid':args})
 24     else:
 25         obj = Class_form(request.POST)
 26         if obj.is_valid():
 27             models.Classes.objects.filter(id=args).update(**obj.cleaned_data)
 28             return redirect('/classes/')
 29         else:
 30             return render(request, 'class_edit.html', {'obj': obj, 'nid': args})
 31 
 32 
 33 def students(request):
 34     if request.method=='GET':
 35         students=models.Student.objects.all()
 36         return render(request,'students.html',{'student':students})
 37 
 38 def students_add(request,nid):
 39     if request.method=='GET':
 40         obj=Students()
 41         return render(request,'student_add.html',{'obj':obj,'nid':nid})
 42     else:
 43         obj=Students(request.POST)
 44         if obj.is_valid():
 45             models.Student.objects.create(**obj.cleaned_data)
 46             return redirect('/students/')
 47         else:
 48             return render(request,'student_add.html',{'obj':obj,'nid':nid})
 49 
 50 
 51 def students_edit(request,nid):
 52     if request.method=='GET':
 53         # print(models.Student.objects.filter(id=nid).values('name','sex','cls_id').first())
 54         obj=Students(initial=models.Student.objects.filter(id=nid).values('name','sex','cls_id').first())
 55         return render(request,'student_edit.html',{'obj':obj,'nid':nid})
 56     else:
 57         obj1=Students(request.POST)
 58         if obj1.is_valid():
 59             models.Student.objects.filter(id=nid).update(**obj1.cleaned_data)
 60             return redirect('/students/')
 61         else:
 62             return render(request, 'student_edit.html', {'obj': obj1, 'nid': nid})
 63 
 64 def teacher(request):
 65     if request.method=='GET':
 66         teacher_list=models.teacher.objects.all()
 67         return render(request,'teacher_list.html',{'tlist':teacher_list})
 68 
 69 def teacher_add(request):
 70     if request.method=='GET':
 71         print(request.method)
 72         obj=teacher_form()
 73         return render(request,'teacher_add.html',{'obj':obj})
 74     else:
 75         obj=teacher_form(request.POST)
 76         if obj.is_valid():
 77             classe_ids=obj.cleaned_data.pop('classes') #老师任教的班级
 78             tname=obj.cleaned_data   #老师的姓名
 79             create_teacher=models.teacher.objects.create(**tname)
 80             create_teacher.c2t.add(*classe_ids)
 81             return redirect('/teachers/')
 82         else:
 83             return render(request,'teacher_add.html',{'obj':obj})
 84 
 85 
 86 def teacher_edit(request,arg):
 87     if request.method=='GET':
 88         teacher_obj=models.teacher.objects.filter(id=arg).first()
 89         classes_ids=teacher_obj.c2t.values_list('id')
 90         cid_list=list(zip(*classes_ids))[0] if list(zip(*classes_ids)) else[]
 91         # print(cid_list) 元组
 92         obj=teacher_form(initial={'tname':teacher_obj.tname,'classes':cid_list})
 93         return render(request,'teacher_edit.html',{'obj':obj,'nid':arg} )
 94     else:
 95         obj=teacher_form(request.POST)
 96         if obj.is_valid():
 97             teacher_name=obj.cleaned_data.pop('tname')
 98             models.teacher.objects.filter(id=arg).update(tname=teacher_name)
 99             obj1=models.teacher.objects.filter(id=arg).first()
100             classes=obj.cleaned_data.pop('classes')
101             obj1.c2t.set(classes)
102             return redirect('/teachers/')
103         else:
104             return render(request,'teacher_add.html',{'obj':obj})
105 
106 
107 def test(request):
108     if request.method=='GET':
109         obj=Test_form(initial={'holby':[ 1,2],'name':'张根','text':'我来自太行山','sex':1})
110         # # print(models.Student.cls.objects.values_list())
111         # obj=models.Student.objects.filter(id=1).first()
112         # print(obj.cls.title)
113         return render(request,'test.html',{'obj':obj})
View Code

form组件

 1 from django.shortcuts import render,HttpResponse,redirect
 2 from  app01 import models
 3 from django.forms import Form,fields
 4 from django.forms import widgets
 5 
 6 class Class_form(Form):
 7     title=fields.RegexField('全栈\d+',
 8                             # initial='全栈', #设置input标签中的默认值
 9                             min_length=2,
10                             required=True,
11                             error_messages={'invalid':"必须以全栈开头",
12                                             'min_length':'太短了',
13                                             'required':"不能为空",
14                                             }
15                             )
16 class Students(Form):
17     name=fields.CharField(required=True,
18     widget=widgets.TextInput(attrs={'class':'form-control'}),
19     error_messages={'required':'姓名不能为空'})
20     sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
21     widget = widgets.TextInput(attrs={'class': 'form-control'})
22 
23                          )
24     cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
25     attrs = {'class': 'form-control'}
26 
27                                                      ),
28 
29                                 )
30 class teacher_form(Form):
31     tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
32    #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
33    #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
34    #      attrs={'multiple':'multiple'})
35    #                           )
36    #  classes=fields.CharField(widget=widgets.SelectMultiple(
37    # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
38    #   choices= models.Classes.objects.values_list('id','title')) )
39     classes=fields.MultipleChoiceField(
40         choices=models.Classes.objects.values_list('id','title'),
41         widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
42     def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
43         super(teacher_form,self).__init__(*args,**kwargs)
44         self.fields['classes'].choices=models.Classes.objects.values_list('id','title')
45 
46 
47 
48 class Test_form(Form):
49     name=fields.CharField()  #动态生成 text类型的input标签
50     text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
51     age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
52     holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
53         choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
54         widget=widgets.CheckboxSelectMultiple)
55     sex=fields.MultipleChoiceField(
56         choices=[(1,''),(2,'')],
57         widget=widgets.RadioSelect
58     )
59 
60     select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
61                                                                    # 通过form组件的MultipleChoiceField字段
62     mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
63                                  widget=widgets.SelectMultiple)
64     file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code

模板

班级管理(1对1)

班级显示

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>班级列表</title>
 6     <link rel="stylesheet" href="/static/bootstrap-3.3.5-dist/css/bootstrap.css">
 7 </head>
 8 <body>
 9 <div style="width: 500px;margin: 0 auto">
10 <h1>班级列表</h1>
11 <ul>
12     {% for obj in clist %}
13         <li>{{ obj.title }}
14             <a href="/classes_add{{ obj.id }}/">添加</a>
15             <a href="/classes_edit{{ obj.id }}/">编辑</a>
16         </li>
17     {% endfor %}
18 </ul>
19 </div>
20 </body>
21 </html>
View Code

添加班级

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加班级</title>
</head>
<body>
<h1>添加班级</h1>
<form action='/classes_add{{2}}/' method="post" novalidate>
    {% csrf_token %}
  <p>班级添加:{{ obj.title}} {{ obj.errors.title.0}}</p>
   <p><input type="submit" value="提交"></p>
</form>

</body>
</html>
View Code

编辑班级

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>编辑班级</title>
</head>
<body>
<form action="/classes_edit{{nid}}/" method="post">
    {% csrf_token %}
   <p>编辑班级:{{ obj.title }}{{obj.title.errors.0}} </p>
    <input type="submit" value="提交">
</form>
</body>
</html>
View Code

 

学生管理1对多

学生显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生管理</title>
</head>
<body>
<h1>学生管理</h1>
<ul>
    {% for row in student %}
        <li>{{row.name}}
            {{row.sex}}
            <a href="/students_add{{row.id}}/">添加</a>
            <a href="/students.edit{{row.id}}/">编辑</a>
        </li>
    {% endfor %}
</ul>
</body>
</html>
View Code

学生添加

<!DOCTYPE html>
<html lang="en">
<head>
    <h1>添加学生</h1>
    <meta charset="UTF-8">
    <title>添加学生</title>
</head>
<body>
<form action="/students_add{{ nid}}/" method="post" novalidate>
     {% csrf_token%}
<p>姓名:{{ obj.name}}{{obj.name.errors.0}}</p>
<p>性别:{{ obj.sex}}{{obj.sex.errors.0}} </p>
<p>班级:{{ obj.cls_id }}{{ obj.errors.0}}  </p>
<p><input type="submit" value="提交"></p>
 </form>
</body>
</html>
View Code

学生编辑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师编辑</title>
</head>
<body>
<form action="/teacher_edit{{ nid }}/" method="post">
    {% csrf_token %}
<p>老师姓名:{{obj.tname }}</p>
<p>班级:{{ obj.classes }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
View Code

 

 老师管理多对多

老师显示

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>老师列表</title>
 6 </head>
 7 <body>
 8 <table border="1">
 9 <thead>
10 <tr><td>ID</td><td>老师</td><td>任教班级</td><td colspan="3">操作</td></tr>
11 </thead>
12 <tbody>
13      {% for teacher in tlist %}
14     <tr>
15         <td>{{ teacher.id }}</td>
16         <td>{{teacher.tname}}</td>
17         <td>
18             {% for row in teacher.c2t.values %}
19             {{ row.title }}
20             {% endfor %}
21          </td>
22         <td><a href="/teacher_add/">添加</a></td>
23         <td><a href="/teacher_edit{{ teacher.id}}/">编辑</a></td>
24     </tr>
25     {% endfor %}
26 </tbody>
27 </table>
28 </body>
29 </html>
View Code

老师添加

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加老师</title>
</head>
<body>
<form action="/teacher_add/" method="post" novalidate>
{% csrf_token %}
<p>老师姓名:{{ obj.tname }}{{obj.tname.errors.0}}</p>
<p>任教班级:{{ obj.classes }}{{obj.classes.errors.0}}</p>
<p><input type="submit" value="提交"></p>
 </form>
</body>
</html>
View Code

老师编辑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师编辑</title>
</head>
<body>
<form action="/teacher_edit{{ nid }}/" method="post">
    {% csrf_token %}
<p>老师姓名:{{obj.tname }}</p>
<p>班级:{{ obj.classes }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
View Code

 PS:Form验证组件验证ajax提交的json

Django的form组件,不仅可以对 浏览器端 提交过来的form表单数据做验证,还可以对ajax提交的 json数据 做验证,但是需要在发送之前获取CSRF-tocken设置在请求头中;

 1 $.ajax({
 2                 type: 'POST',
 3                 async: false,
 4                 cache: false,
 5                 url: '{% url "instance_add" %}',
 6                  headers:{"X-CSRFToken":$.cookie('csrftoken')},
 7                 contentType: "application/json; charset=utf-8",
 8                 dataType: "json",
 9                 traditional:true,
10                 data:JSON.stringify({
11                     "data_mode_type": $data_mode_type,
12                     'database_type': $database_type,
13                     'host': $host,
14                     'port': $port,
15                     'instance_nikename': $instance_nikename,
16                     'db_business': $db_business,
17                     'DBA': $DBA,
18                     'responsible_person': $responsible_person
19                 }),
20                 success: function (data) {
21                    alert(1)
22                 }
23             });
24         })
前端
from django.shortcuts import render,HttpResponse,redirect
from DB_auto.form_validate.add_dbinfo import dbinfo_create
import json

def instance_add(request):
    if request.method=='POST':
        json_data=json.loads(request.body.decode('utf-8'))
        obj=dbinfo_create(json_data)
        if obj.is_valid():
            print(obj.cleaned_data)
        else:
            print(obj.errors)
    return render(request,'add_dbinfo.html')
django后台

 

 七、ModelForm

ModelForm的简单使用流程:

class Book(models.Model):
       nid=models.AutoField(primary_key=True)
       title=models.CharField(max_length=32)
             
       price=models.DecimalField(max_digits=8,decimal_places=2) # 999999.99
       pub_date=models.DateTimeField()  # "2012-12-12"
        
publish=models.ForeignKey(to="Publish",on_delete=models.CASCADE)  # 级联删除
       authors=models.ManyToManyField(to="Author")
       def __str__(self):
         return self.title
models.py
from django import forms
class BookModelForm(forms.ModelForm):
     class Meta:
      model=Book     #对应的Model中的类
      fields="__all__"    #字段,如果是__all__,就是表示列出所有的字段
                        
'''
BookModelForm等同于:
class BookForm(forms.Form):
       title=forms.CharField(max_length=32)
       price=forms.IntegerField()
             
       pub_date=forms.DateField(widget=widgets.TextInput(attrs={"type":"date"}))
      #publish=forms.ChoiceField(choices=[(1,"AAA"),(2,"BBB")])
             
   publish=forms.ModelChoiceField(queryset=Publish.objects.all())
    authors=forms.ModelMultipleChoiceField(queryset=Author.objects.all())
'''
class Meta中还可以有一些设置:
        # fields=["title","price","pub_date"]
        # exclude=["title"]   #排除的字段
        labels={
            "title":"书籍名称",
            "price":"价格"
        },
        error_messages={
            "title":{"required":"不能为空"},
        },
       from django.forms import widgets as wid
        widgets={
            "pub_date":wid.TextInput(attrs={"type":"date"}),
        }

ModelForm还可以自定义初始化方法和自定义钩子:
    def clean_price(self):
        # 定义局部钩子
        pass
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        for filed in self.fields.values():
            #filed.error_messages={"required":"不能为空"}
            filed.widget.attrs.update({'class': 'form-control'})
app01/modelforms.py
        添加书籍:
         def add(reqeust):
             if request.method == 'GET':
             form=BookModelForm()
             return render(reqeust,{"form":form})
             '''
             渲染页面
             <form action="" method="post" novalidate>
                 {% csrf_token %}
                 {% for field in form %}
                         <div class="form-group">
                     <label for="title">{{ field.label }}</label>
                      {{ field }}
                     <span>{{ field.errors.0 }}</span>
                         </div>
                 {% endfor %}
                 <input type="submit" value="提交" class="btn btn-default pull-right">
             </form>
             '''
              else:    # POST请求
              form=BookModelForm(request.POST)
                          if form.is_valid():
                 form.save() # Book.objects.create(cleaned_data)
                 return redirect("/")
              else:
                 return render(reqeust,{"form":form})
         编辑书籍: 
          def edit(request,id):
             edit_obj=Book.objects.get(pk=id)
             if request.method == 'GET':
                 form=BookModelForm(instance=edit_obj)
                 return render(reqeust,{"form":form})
                 '''
                 渲染页面同添加页面
                              
                 '''
             else:     # POST请求
                 
              form=BookModelForm(request.POST,instance=edit_obj)
                               if form.is_valid():
                     form.save() #  edit_obj.update(clean_data)
                     return redirect("/")
                 else:
                     return render(reqeust,{"form":form})
views.py(包括添加和编辑数据)

 

这是一个神奇的组件,通过名字我们可以看出来,这个组件的功能就是把model和form组合起来,先来一个简单的例子来看一下这个东西怎么用:比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,现在让你写一个创建学生的页面,你的后台应该怎么写呢?首先我们会在前端一个一个罗列出这些字段,让用户去填写,然后我们从后天一个一个接收用户的输入,创建一个新的学生对象,保存其实,重点不是这些,而是合法性验证,我们需要在前端判断用户输入是否合法,比如姓名必须在多少字符以内,电话号码必须是多少位的数字,邮箱必须是邮箱的格式这些当然可以一点一点手动写限制,各种判断,这毫无问题,除了麻烦我们现在有个更优雅(以后在Python相关的内容里,要多用“优雅”这个词,并且养成习惯)的方法:ModelForm先来简单的,生硬的把它用上,再来加验证条件。

创建modelform

#首先导入ModelForm

from django.forms import ModelForm
#在视图函数中,定义一个类,比如就叫StudentList,这个类要继承ModelForm,在这个类中再写一个原类Meta(规定写法,并注意首字母是大写的)
#在这个原类中,有以下属性(部分):

class StudentList(ModelForm):
    class Meta:
        model =Student #对应的Model中的类
        fields = "__all__" #字段,如果是__all__,就是表示列出所有的字段
        exclude = None #排除的字段
        #error_messages用法:
        error_messages = {
        'name':{'required':"用户名不能为空",},
        'age':{'required':"年龄不能为空",},
        }
        #widgets用法,比如把输入用户名的input框给为Textarea
        #首先得导入模块
        from django.forms import widgets as wid #因为重名,所以起个别名
        widgets = {
        "name":wid.Textarea(attrs={"class":"c1"}) #还可以自定义属性
        }
        #labels,自定义在前端显示的名字
        labels= {
        "name":"用户名"
        }

然后在url对应的视图函数中实例化这个类,把这个对象传给前端

def student(request):

    if request.method == 'GET':
        student_list = StudentList()
        return render(request,'student.html',{'student_list':student_list})
        

然后前端只需要 {{ student_list.as_p }} 一下,所有的字段就都出来了,可以用as_p显示全部,也可以通过for循环这
student_list,拿到的是一个个input框,现在我们就不用as_p,手动把这些input框搞出来,as_p拿到的页面太丑。
首先 for循环这个student_list,拿到student对象,直接在前端打印这个student,是个input框student.label ,拿到数据库中每个字段的verbose_name ,如果没有设置这个属性,拿到的默认就是字段名,还可以通过student.errors.0 拿到错误信息有了这些,我们就可以通过bootstrap,自己拼出来想要的样式了,比如:

<body>
<div class="container">
    <h1>student</h1>
    <form method="POST" novalidate>
        {% csrf_token %}
        {# {{ student_list.as_p }}#}
        {% for student in student_list %}
            <div class="form-group col-md-6">
                {# 拿到数据字段的verbose_name,没有就默认显示字段名 #}
                <label class="col-md-3 control-label">{{ student.label }}</label>
                <div class="col-md-9" style="position: relative;">{{ student }}</div>
            </div>
        {% endfor %}
        <div class="col-md-2 col-md-offset-10">
            <input type="submit" value="提交" class="btn-primary">
        </div>
    </form>
</div>
</body>

现在还缺一个input框的form-contral样式,可以考虑在后台的widget里面添加
比如这样:

from django.forms import widgets as wid #因为重名,所以起个别名
widgets = {
"name":wid.TextInput(attrs={'class':'form-control'}),
"age":wid.NumberInput(attrs={'class':'form-control'}),
"email":wid.EmailInput(attrs={'class':'form-control'})
}

当然也可以在js中,找到所有的input框,加上这个样式,也行。

添加纪录

保存数据的时候,不用挨个取数据了,只需要save一下

def student(request):

    if request.method == 'GET':
         student_list = StudentList()
         return render(request,'student.html',{'student_list':student_list})
    else:
         student_list = StudentList(request.POST)
         if student_list.is_valid():
         student_list.save()
         return redirect(request,'student_list.html',{'student_list':student_list})

编辑数据

如果不用ModelForm,编辑的时候得显示之前的数据吧,还得挨个取一遍值,如果ModelForm,只需要加一个instance=obj(obj是要修改的数据库的一条数据的对象)就可以得到同样的效果
保存的时候要注意,一定要注意有这个对象(instance=obj),否则不知道更新哪一个数据
代码示例:

from django.shortcuts import render,HttpResponse,redirect
from django.forms import ModelForm
# Create your views here.
from app01 import models
def test(request):
    # model_form = models.Student
    model_form = models.Student.objects.all()
    return render(request,'test.html',{'model_form':model_form})

class StudentList(ModelForm):
    class Meta:
        model = models.Student #对应的Model中的类
        fields = "__all__" #字段,如果是__all__,就是表示列出所有的字段
        exclude = None #排除的字段
        labels = None #提示信息
        help_texts = None #帮助提示信息
        widgets = None #自定义插件
        error_messages = None #自定义错误信息
        #error_messages用法:
        error_messages = {
        'name':{'required':"用户名不能为空",},
        'age':{'required':"年龄不能为空",},
        }
        #widgets用法,比如把输入用户名的input框给为Textarea
        #首先得导入模块
        from django.forms import widgets as wid #因为重名,所以起个别名
        widgets = {
        "name":wid.Textarea
        }
        #labels,自定义在前端显示的名字
        labels= {
        "name":"用户名"
        }
def student(request):
    if request.method == 'GET':
        student_list = StudentList()
        return render(request,'student.html',{'student_list':student_list})
    else:
        student_list = StudentList(request.POST)
        if student_list.is_valid():
            student_list.save()
            return render(request,'student.html',{'student_list':student_list})

def student_edit(request,pk):
    obj = models.Student.objects.filter(pk=pk).first()
    if not obj:
        return redirect('test')
    if request.method == "GET":
        student_list = StudentList(instance=obj)
        return render(request,'student_edit.html',{'student_list':student_list})
    else:
        student_list = StudentList(request.POST,instance=obj)
        if student_list.is_valid():
            student_list.save()
            return render(request,'student_edit.html',{'student_list':student_list})

总结: 从上边可以看到ModelForm用起来是非常方便的,比如增加修改之类的操作。但是也带来额外不好的地方,model和form之间耦合了。如果不耦合的话,mf.save()方法也无法直接提交保存。 但是耦合的话使用场景通常局限用于小程序,写大程序就最好不用了。

 

Form组件和ModelForm的区别

ModelForm是Django Model.py和Form组件的结合体,可以简单/快速使用 Form验证和数据库操作功能,但不如Form组件灵活,如果在使用Django做web开发过程中验证的数据和数据库字段相关(可以对表进行增、删、改操,注意 Many to many字段,也可以级联操作第3张关系表;),建议优先使用ModelForm,用起来更方便些,但是在使用ModelForm的时候慎用fields='__all__',获取数据库所有字段势必造成性能损耗;

 1 ModelForm
 2     a.  class Meta:
 3             model,                           # 对应Model的
 4             fields=None,                     # 字段
 5             exclude=None,                    # 排除字段
 6             labels=None,                     # 提示信息
 7             help_texts=None,                 # 帮助提示信息
 8             widgets=None,                    # 自定义插件
 9             error_messages=None,             # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)
10             field_classes=None               # 自定义字段类 (也可以自定义字段)
11             localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据
12             如:
13                 数据库中
14                     2016-12-27 04:10:57
15                 setting中的配置
16                     TIME_ZONE = 'Asia/Shanghai'
17                     USE_TZ = True
18                 则显示:
19                     2016-12-27 12:10:57
20     b. 验证执行过程
21         is_valid -> full_clean -> 钩子 -> 整体错误
22  
23     c. 字典字段验证
24         def clean_字段名(self):
25             # 可以抛出异常
26             # from django.core.exceptions import ValidationError
27             return "新值"
28     d. 用于验证
29         model_form_obj = XXOOModelForm()
30         model_form_obj.is_valid()
31         model_form_obj.errors.as_json()
32         model_form_obj.clean()
33         model_form_obj.cleaned_data
34     e. 用于创建
35         model_form_obj = XXOOModelForm(request.POST)
36         #### 页面显示,并提交 #####
37         # 默认保存多对多
38             obj = form.save(commit=True)
39         # 不做任何操作,内部定义 save_m2m(用于保存多对多)
40             obj = form.save(commit=False)
41             obj.save()      # 保存单表信息
42             obj.save_m2m()  # 保存关联多对多信息
43  
44     f. 用于更新和初始化
45         obj = model.tb.objects.get(id=1)
46         model_form_obj = XXOOModelForm(request.POST,instance=obj)
47         ...
48  
49         PS: 单纯初始化
50             model_form_obj = XXOOModelForm(initial={...})
Django之ModelForm组件(wupeiqi)

使用ModelForm

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{ form_obj.as_p }}
{#<p>姓名:{{form_obj.name  }}</p>#}
</body>
</html>
View Code

后端视图:

 1 from app02 import models
 2 from django.forms import ModelForm
 3 class UserModalForm(ModelForm):
 4     class Meta:
 5         model=models.UserInfo #(该字段必须为 model  数据库中表)
 6         fields= '__all__'   #(该字段必须为 fields 数据库中表)
 7 
 8 def add(request):
 9      # 实例化models_form
10     if request.method=='GET':
11         obj = UserModalForm()
12         return render(request,'rbac/user_add.html',locals())
13     else:
14         obj=UserModalForm(request.POST)
15         if obj.is_valid():
16             data=obj.cleaned_data
17             obj.save()  #form验证通过直接 添加用户信息到数据库
18         return render(request, 'rbac/user_add.html', locals())
View Code

 model.py:

使用使用Form组件和ModelForm注意事项:

1、model.py一点要写verbose_name='名称'参数,才会在前端显示P标签的标题;

from django.db import models

class Department(models.Model):
    title=models.CharField(max_length=32,verbose_name='部门名称')
    def __str__(self):
        return self.title

class UserInfo(models.Model):
    name=models.CharField(max_length=32,verbose_name='姓名')
    emai=models.EmailField(max_length=32,verbose_name='邮箱')
    pary=models.ForeignKey(Department,verbose_name='部门')
View Code

如果使用Form组件在前端显示标题,可以设置Form类中的lable参数

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(choices=[(1,"技术部"),(2,'销售部'),(3,'市场部'),],label='部门')
View Code

2、pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部门'),前端select标签不能随数据库操作实时更新;

在Department表添加一条数据之后,前端select标签中的数据不能随数据库实时更新;

原因:

不管是ModelForm还是Form组件本质就是个类,fields(字段)本质就是类中的1个静态属性,在类第一次加载时赋值,永远不会更新;

 

解决方案1(手动档):

重写__init__方法,每次实例化对象,就去获取一次数据库内容,重新赋值;

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(label='部门')
    # pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部门')
    def __init__(self,*args,**kwargs):
        super(UserInfoForm,self).__init__(*args,**kwargs)
        self.fields['pary'].choices=models.Department.objects.values_list('pk','title')
View Code

解决方案2(自动档)

导入ModelChoiceField 模块  from django.forms.models import ModelChoiceField 

使用ModelChoiceField 字段

from django.forms import fields
from django.forms import widgets
from django.forms import ModelForm
from django.forms.models import ModelChoiceField

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    #方案1
    pary=ModelChoiceField(queryset=models.Department.objects.all(),label='部门')
View Code

自动挡虽好,但是也有缺陷;前端option标签的内容,需要借助model.py中的__str__方法,生成;

八、扩展 widgets之富文本编辑框

 如果你的web应用涉及到了发表文章、表情评论就必须使用富文本编辑框,小编调研了两款 Kindeditor、CKeditor;

Kindeditor

1.Kindeditor简介

http://kindeditor.net/docs/usage.html

2.下载Kindeditor编辑器

http://www.kindsoft.net/down.php

3.引入Kindeditor编辑器

<script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script>

4.使用Kindeditor编辑器

a.创建textarea 标签

<textarea name="content" id="a" cols="30" rows="10"></textarea>

b.初始化Kindeditor

<script>
  var edit=KindEditor.create('#a',{
        width:'700px',
        height:'500px',
        uploadJson:'/upload_file/',
        extraFileUploadParams:{
            'csrfmiddlewaretoken':'{{ csrf_token }}',}})
</script>

c. Kindeditor功能定制

<script>
        var edit = KindEditor.create('#a', {
            items: [ //填写自己想要的功能
                'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline',
                'removeformat', '|', 'justifyleft', 'justifycenter', 'justifyright', 'insertorderedlist',
                'insertunorderedlist', '|', 'emoticons', 'image', 'link'],
            width: '700px',
            height: '500px',
            uploadJson: '/upload_file/',
            extraFileUploadParams: {
                'csrfmiddlewaretoken': '{{ csrf_token }}',
            }
        })
    </script>

d.kindeditor的API

// 取得HTML内容
html = editor.html();

// 同步数据后可以直接取得textarea的value
editor.sync();
html = document.getElementById('editor_id').value; // 原生API
html = K('#editor_id').val(); // KindEditor Node API
html = $('#editor_id').val(); // jQuery

// 设置HTML内容
editor.html('HTML内容');

 

CKeditor结合Django Form

1.下载  django-ckeditor 和pillow

pip install django-ckeditor 
pip install pillow

2.setings

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rbac.apps.RbacConfig',
    'arya.apps.AryaConfig',
    'cmdb.apps.CmdbConfig',
    'rest_framework',
    'ckeditor',
    # 'ckeditor_uploader'
]
CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': (
            # ['div','Source','-','Save','NewPage','Preview','-','Templates'],
            # ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print','SpellChecker','Scayt'],
            # ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
            # ['Form','Checkbox','Radio','TextField','Textarea','Select','Button', 'ImageButton','HiddenField'],
            # ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
            # ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
            # ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
            # ['Link','Unlink','Anchor'],
            ['Smiley',],

            # ['Styles','Format','Font','FontSize'],
            # ['TextColor','BGColor'],
            # ['Maximize','ShowBlocks','-','About', 'pbckcode'],

        ),
    }
}
功能定制

3. 收集Django 项目所有 静态文件  到的目录static目录   /   直接下载ckeditor到项目static

 设置media路径+static路径

注意:此步骤如果提示 需要你设置MEDIA_URL目录 和STATIC_ROOT 就先暂且设置一下,最终目的是把CKeditor的js插件copy到你的静态文件存放目录下;

4.后端生成 textarea +加前端 ckedit 插件生成 富文本编辑

from ckeditor.widgets import CKEditorWidget
class comment(Form): #评论框
    content =fields.CharField(widget=CKEditorWidget)
{{ obj.content|safe }}

<script src="/static/pligin/ckeditor/ckeditor-init.js"></script>
<script src="/static/pligin/ckeditor/ckeditor/ckeditor.js"></script>
View Code

5.CKEditorWidget API

  <script>
        $('.root_reply_btn').click(function () {
            var $reply_user = $(this).attr('name');
            var $pid = $(this).attr('pid');
            $('#comment_submit').attr('pid', $pid);
            CKEDITOR.instances.id_content.insertText('回复' + $reply_user + ':');
            CKEDITOR.instances.id_content.insertHtml('<br>');
            CKEDITOR.instances.id_content.focus(); #使CKEditorWidget进入编辑状态 (MD在后台测试了半天!!)


        });
CKEDITOR.instances.id_content.document.$.body.firstChild.textContent 获取p标签的文本内容 </script>

6.完成效果

博客链接:http://www.cnblogs.com/wupeiqi/articles/6144178.html

                  https://www.cnblogs.com/SHENGXIN/p/7643554.html

                  http://www.cnblogs.com/yuanchenqi/articles/7439088.html#3770465

                  DjangoForm补充:http://www.cnblogs.com/yuanchenqi/articles/7487059.html

posted on 2019-06-04 19:27  始终不够啊  阅读(422)  评论(0编辑  收藏  举报