Django之Form组件(***)

Django之Form组件

  Form组件,用来做一些数据的提前验证,比如登录注册中,我们定义的是邮箱登录,但是用户却手机号登录,那么用Form组件去实现这个验证过程的话,数据的判断逻辑就不会去数据库匹配啦,这样反而会减轻数据库的压力。

  所以,Form组件的功能是对用户的请求数据库做验证的。并且对获取的数据也可以做验证功能。  

  以往的登录注册中的问题:

    在以往的登录注册页面中,如果有输入错误,页面是重新刷新的,所以之前输入对的数据也会随之消失。

    重复进行用户数据的校验:正则,长度,是否为空等等。

    Form组件的解决方法:

    先导入模块:  

from django.forms import Form
from django.forms import fields 

  后端:

from django.forms import Form
from django.forms import fields

class LoginForm(Form):
“““
字段的名称必须与前端的字典名称一致。
””” username
= fields.CharField( max_length=18, min_length=6, required=True, error_messages={ "required":"用户名不能为空", "min_length":"长度不能小于6位", "max_length":"长度不能大于18位", } ) password = fields.CharField( min_length=16, required=True, error_messages={ "required": "密码不能为空", "min_length": "长度不能小于16位", } ) def login(request): if request.method == "GET": return render(request,"login.html") else: obj = LoginForm(request.POST) ret = obj.is_valid() #内部自动校验,ret是False和True的结果 if ret: #用户输入格式正确 print(obj.cleaned_data) #cleaned_data 是一个字典类型,是校验成功后拿到的值。 return redirect('http://www.baidu.com') else: #用户输入格式错误 # print(obj.errors) #所有的错误信息,这个错误信息是一个对象,是 __str__ 方法 # print(obj.errors["username"]) # print(obj.errors["username"][0]) #在LoginForm类中定义了多个条件,那么就可能有多个错误信息,所有可以用索引取第一个 # print(obj.errors["password"]) # print(obj.errors["password"][0]) return render(request,"login.html",{"obj":obj})

  前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>登录</h1>
    <form method="post" action="login.html">
        {% csrf_token %}
        <p>username:<input type="text" name="username">{{ obj.errors.username.0 }}</p>
        <p>password:<input type="password" name="password">{{ obj.errors.password.0 }}</p>
        <input type="submit" value="submit">
    </form>
</body>
</html>

   到目前为止,Form组件的功能是对用户提交的数据进行校验和保留上次输入的内容。

   对于前端用户提交的方式,有两种,一个是Form提交,一种是Ajax提交。 

   其中Form提交是刷新,并失去上次的输入内容。

   而Ajax是不刷新,保留上次输入的内容。

   Form的验证流程:

    当执行is_valid的时候,Form的验证原理是:

      1. 获取当期类中所有的字段,也就是LoginForm类中,我们定义的字段。  

             也就是当每次对LoginForm实例化的时候,会将LoginForm类中的字段放到self.fields中。这个self.fields 类似一个字典。

       self.fields = {

        "username":fields.CharField(required=Ture),  #这个对象包含了这个正则表达式。

        "password":fields.CharField(required=Ture),  #这个对象包含了这个正则表达式。

       }

      2. 会循环self.fields,被循环的个数是由LoginForm来定的,因为self.fields循环的就是LoginForm的字段的个数。

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

            k:k是每次循环的username或password,它是字符串类型。

          v:v是对应的正则表达式。

          input_value = request.POST.get(k):这个表达式的意思是POST里有很多数据。

                           这里的k是字段的名字,如何写username,那么就会去找username的数据。

          v的正则表达式的值和input_value的值进行校验。

        所以在for循环之前,先定义一个flag=Ture.

        当v的正则和input_value校验不匹配时,返回False的值。

flag = Ture
for k,v in self.fields.items():
    input_value = request.POST.get(k)
    flag = False
return flag

  以上的Form流程就是is_valid的原理。

    is_valid 是True的话,获取cleaned_data的数据。

   Form组件和Ajax的提交验证:

    用Ajax的形式将前端的数据发送给后台:

    前端代码:  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户登录</h1>
    <form id="f1" method="post" action="login.html">
        {% csrf_token %}
        <p>username:<input type="text" name="username">{{ obj.errors.username.0 }}</p>
        <p>password:<input type="password" name="password">{{ obj.errors.password.0 }}</p>
        <input type="submit" value="submit">
        <a onclick="submitForm();">提交</a>
    </form>
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function submitForm() {
            $.ajax({
                url:"/ajax_login/",
                type:"POST",
                data:$("#f1").serialize(),   //serialize()他会将所有的input框的数据打包交给data。这里打包的有csrf和username,password。效果是username=xxx&password=xxx&csrf=xxx,这是个字符串。
                success:function (arg) {
                    console.log(arg)
                }
            })
        }
    </script>
</body>
</html>

    后端代码:

def ajax_login(request):
    obj = LoginForm(request.POST)
    if obj.is_valid():
        print(obj.cleaned_data)
    else:
        print(obj.errors)
    return HttpResponse("...")

    这样就算是提交错误了,页面是不会刷新,并且会保留之前输入的数据。

    但这里我们实现代码会发现一个问题,那就是在错误提交数据后,也是不刷新啦,前端输入的值后端也拿到了,但是但是,有个问题点,就是前端的页面并没有错误提示呀。

    所以,这个ajax的提交并不是那么的那么的完美,所以,我们接下来,就来完善一下这个ajax提交的错误提示。

  Form组件和Ajax提交验证的显示错误信息:

    这里用到了json.dumps的序列化操作,序列化的是obj.errors的对象。

    前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户登录</h1>
    <form id="f1" method="post" action="login.html">
        {% csrf_token %}
        <p>username:<input type="text" name="username">{{ obj.errors.username.0 }}</p>
        <p>password:<input type="password" name="password">{{ obj.errors.password.0 }}</p>
        <input type="submit" value="submit">
        <a onclick="submitForm();">提交</a>
    </form>
    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        function submitForm() {
            $('.c1').remove();
            $.ajax({
                url:"/ajax_login/",
                type:"POST",
                data:$('#f1').serialize(),   //serialize()他会将所有的input框的数据打包交给data。这里打包的有csrf和username,password。效果是username=xxx&password=xxx&csrf=xxx,这是个字符串。
                dataType:"JSON",
                success:function (arg) {
                    console.log(arg);
                    if(arg.status){

                    }else {
                         $.each(arg.msg,function(index,value){
                            console.log(index,value);   //index是对应的k的值,也就是username,value是username输入的值。
                            var tag = document.createElement("span");
                            tag.innerHTML = value[0];
                            tag.className = 'c1';
                            $('#f1').find('input[name="'+ index +'"]').after(tag);
                        })
                    }
                }
            })
        }
    </script>
</body>
</html>

    后端代码:

def ajax_login(request):
    import json
    ret = {'status': True,'msg': None}
    obj = LoginForm(request.POST)
    if obj.is_valid():
        print(obj.cleaned_data)
    else:
        # print(obj.errors) # obj.errors对象
        ret['status'] = False
        ret['msg'] = obj.errors
    v = json.dumps(ret)
    return HttpResponse(v)

   这样就可以实现ajax提交有错误提示啦。

 

  Form组件的常见字段和参数:

    在XXXForm类中的字段:

    数字类型:

#数字类型
number = fields.IntegerField(
min_value=10, #这里的min和max不是单纯的数字长度,而是数字范围。
max_value=1000,
#有错误信息 error_messages={ "required":"number不能为空", "invalid":"number格式错误,必须是数字", #invalid 格式错误
"min_value":"数值必须大于10",
"max_value":"数值必须小于1000", } )   

    邮件类型:

#邮件类型
Email = fields.EmailField(
    #有错误信息
    error_messages={
        "required":"Email不能为空",
        "invalid":"Email格式错误,必须是邮件格式",  #invalid 格式错误
    }
) 

    还有对URL的fields.URLField。

    还有fields.SlugField 和fields.GenericIPAddressField、fields.DateField、fields.DateTimeField。

    但这些内置的也会有不完善的地方,无法达到我们的需求。所以,还有fields.RegesField。

    可以用这个fields.RegesField去写正则表达式。  

test = fields.RegesField('185\d+')

    fields.RegesField是继承CharField的。

    而CharField也是继承的Field,所以,我们来看一下Field的里的参数:

  Form组件的Field类的参数:

    widget 是用来指定生成什么样的HTML标签。比如select,text,input。

    但用widget要先导入:from django.forms import widgets

 

     label:写什么就在前端页面显示什么,在前端的写法是:   

{{ obj.字段名.label }}
  好比:
    {{ obj.t1.label }}

    initial:初始值。

    在input框中显示默认值用的。

    help_text:提供帮助信息:

在Form的字段中定义help_text:
    help_text='.......'
在前端代码和label一样:
{{ obj.t1.help_text }}

这样就可以在前端显示:.......啦

    validators:自定义验证规则

validators=[ ]

    localize=False:是否支持本地化,这是用来转化时间的。

    disabled=False:是否可以编辑。

    label_suffix=None:label内容后缀。

    上述这里参数,处理validators以外。其余的一起用,是可以自动生成HTML标签的。

    这里就实现一个用后端参数实现一个前端标签的代码:

    后端:

class TestForm(Form):
    t1 = fields.CharField(
        required=True,
        label="usename",
        label_suffix=":",
        help_text="输入username",
        disabled=False,
        initial="username",
        max_length=8,
        min_length=2,
        error_messages={
            "required":"不能为空",
            "max_length":"long",
            "min_length":"",
        }

    )

def login(request):
    if request.method =="POST":
        obj = TestForm()
        return render(request,"login.html",{"obj":obj})
    else:
        obj = TestForm(request.POST)
        if obj.is_valid():
            print(obj.cleaned_data)
        else:
            print(obj.errors)
        return render(request,"login.html",{"obj":obj})

    前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户登录</h1>
    <form id="f1" method="post" action="login.html">
        {% csrf_token %}
        <p>
            {{ obj.t1.label }}{{ obj.t1.label_suffix }}
            {{ obj.t1 }}{{ obj.t1.help_text }}
        </p>
        <input type="submit" value="submit">
    </form>
</body>
</html>

    以上就是用后端代码实现前端的标签的。

  Form小总结:

    form有验证的功能:  

      1.类:

        字段 = 正则

      2.is_valid()

    form有生成HTML标签的功能:  

        1.类:

        字段 = 正则( 正则这里规定一些生成HTML标签的特性)

      2.is_valid()

  Form组件保留上次输入的内容:

    前端:  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

    <form action="/register/" method="POST" novalidate>
        {% csrf_token %}
        <p>user:
            {{ obj.user }} {{ obj.errors.user.0 }}
        </p>
        <p>email:
            {{ obj.email }} {{ obj.errors.email.0 }}
        </p>
        <p>password:
            {{ obj.password }} {{ obj.errors.password.0 }}
        </p>
        <p>phone:
            {{ obj.phone }} {{ obj.errors.phone.0 }}
        </p>
        <input type="submit" value="提交"  />
    </form>
</body>
</html>

    后端:

class RegiterForm(Form):
    user = fields.CharField(min_length=8)
    email = fields.EmailField()
    password = fields.CharField()
    phone = fields.RegexField('139\d+')

def register(request):
    if request.method == 'GET':
        obj = RegiterForm()
        return render(request,'register.html',{'obj':obj})  #这个obj里是没有值的,因为是第一次的请求
    else:
        obj = RegiterForm(request.POST)
        if obj.is_valid():
            print(obj.cleaned_data)
        else:
            print(obj.errors)
        return render(request,'register.html',{'obj':obj})   #这里的obj有值,值是input输入的值。

   这样输入的值就可以保留。

   Form组件小示例班级学生老师管理:

     示例中,应用到的Form特性,以后在任何的提交数据和编辑数据中,都应该应用Form组件去完成,因为我们是不信任提交的数据的。

       所有,需要用Form去做验证。

      后端views代码:    

from django.shortcuts import render,redirect
from app01 import models
from django.forms import Form
from django.forms import fields
from django.forms import widgets


class ClassForm(Form):
    title = fields.RegexField('全栈\d+')

def class_list(request):
    cls_list = models.Classes.objects.all()
    return render(request,'class_list.html',{'cls_list':cls_list})

def add_class(request):
    if request.method == "GET":
        obj = ClassForm()
        return render(request,'add_class.html',{'obj': obj})
    else:
        obj = ClassForm(request.POST)
        if obj.is_valid():
            # obj.cleaned_data # 字典
            # 数据库创建一条数据
            # print(obj.cleaned_data)
            # models.Classes.objects.create(title=obj.cleaned_data['tt'])

            models.Classes.objects.create(**obj.cleaned_data)
            return redirect('/class_list/')
        return render(request,'add_class.html',{'obj': obj})

def edit_class(request,nid):
    if request.method == "GET":
        row = models.Classes.objects.filter(id=nid).first()
        # 让页面显示初始值
        # obj = ClassForm(data={'title': 'asdfasdfasdfas'})
        obj = ClassForm(initial={'title': row.title})
        return render(request,'edit_class.html',{'nid': nid,'obj':obj})
    else:
        obj = ClassForm(request.POST)
        if obj.is_valid():
            models.Classes.objects.filter(id=nid).update(**obj.cleaned_data)
            return redirect('/class_list/')
        return render(request,'edit_class.html',{'nid': nid,'obj':obj})

class StudentForm(Form):
    name = fields.CharField(
        min_length=2,
        max_length=6,
        widget=widgets.TextInput(attrs={'class': 'form-control'})
    )
    email = fields.EmailField(widget=widgets.TextInput(attrs={'class': 'form-control'}))
    age = fields.IntegerField(min_value=18,max_value=25,widget=widgets.TextInput(attrs={'class': 'form-control'}))
    cls_id = fields.IntegerField(
        # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')])
        widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'})
    )

def student_list(request):
    stu_list = models.Student.objects.all()
    return render(request,'student_list.html',{'stu_list':stu_list})

def add_student(request):
    if request.method == "GET":
        obj = StudentForm()
        return render(request,'add_student.html',{'obj':obj})
    else:
        obj = StudentForm(request.POST)
        if obj.is_valid():
            models.Student.objects.create(**obj.cleaned_data)
            return redirect('/student_list/')
        return render(request,'add_student.html',{'obj':obj})

def edit_student(request,nid):
    if request.method == "GET":
        row = models.Student.objects.filter(id=nid).values('name','email','age','cls_id').first()
        obj = StudentForm(initial=row)
        return render(request,'edit_student.html',{'nid':nid,'obj': obj})
    else:
        obj = StudentForm(request.POST)
        if obj.is_valid():
            models.Student.objects.filter(id=nid).update(**obj.cleaned_data)
            return redirect('/student_list/')
        return render(request,'edit_student.html',{'nid':nid,'obj': obj})
View Code

      后端models代码:

from django.db import models

class Classes(models.Model):
    title = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    age = models.IntegerField(max_length=32)
    cls = models.ForeignKey('Classes')

class Teacher(models.Model):
    tname = models.CharField(max_length=32)
    c2t = models.ManyToManyField('Classes')
View Code

     URL:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^class_list/', views.class_list),
    url(r'^add_class/', views.add_class),
    url(r'^edit_class/(\d+)/', views.edit_class),

    url(r'^student_list/', views.student_list),
    url(r'^add_student/', views.add_student),
    url(r'^edit_student/(\d+)/', views.edit_student),
]
View Code

     前端class_list 代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>班级列表</h1>
    <div>
        <a href="/add_class/">添加</a>
    </div>
    <ul>
        {% for row in cls_list %}
            <li>{{ row.title }} <a href="/edit_class/{{ row.id }}/">编辑</a>  </li>
        {% endfor %}
    </ul>
</body>
</html>
View Code

    前端add_class 代码:

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

    前端edit_class 代码:

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

    前端student_list 代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>学生列表</h1>
    <a href="/add_student/">添加</a>
    <ul>
        {% for row in stu_list %}
            <li>{{ row.name }}-{{ row.email }}-{{ row.age }}-{{ row.cls_id }}-{{ row.cls.title }}   <a href="/edit_student/{{ row.id }}/">编辑</a></li>
        {% endfor %}
    </ul>
</body>
</html>
View Code

    前端add_student 代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>添加学生</h1>
    <form action="/add_student/" method="POST">
        {% csrf_token %}
        <p>
            {{ obj.name }}
        </p>
        <p>
            {{ obj.email }}
        </p>
        <p>
            {{ obj.age }}
        </p>
        <p>
            {{ obj.cls_id }}
        </p>
        <input type="submit" value="提交" />
    </form>
</body>
</html>
View Code

    前端edit_student 代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.5-dist/css/bootstrap.css"/>
</head>
<body>


    <div style="width: 500px;margin: 0 auto;">
        <form class="form-horizontal" method="POST" action="/edit_student/{{ nid }}/">
            {% csrf_token %}
            <div class="form-group">
                <label class="col-sm-2 control-label">姓名:</label>

                <div class="col-sm-10">
                    {{ obj.name }}
                </div>
            </div>
            <div class="form-group">
                <label class="col-sm-2 control-label">邮箱:</label>

                <div class="col-sm-10">
                    {{ obj.email }}
                </div>
            </div>
             <div class="form-group">
                <label class="col-sm-2 control-label">年龄:</label>

                <div class="col-sm-10">
                    {{ obj.age }}
                </div>
            </div>
             <div class="form-group">
                <label class="col-sm-2 control-label">班级:</label>

                <div class="col-sm-10">
                    {{ obj.cls_id }}
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-10">
                    <input type="submit" class="btn btn-default" value="提交" />
                </div>
            </div>
        </form>
    </div>
</body>
</html>
View Code

      老师多对多:

    注意:

            Select框:
                单选
                    cls_id = fields.IntegerField(
                        # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')])
                        widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'})
                    )
                    
                    cls_id = fields.ChoiceField(
                        choices=models.Classes.objects.values_list('id','title'),
                        widget=widgets.Select(attrs={'class': 'form-control'})
                    )
                    
                    
                    obj = FooForm({'cls_id':1})
                多选
                    xx = fields.MultipleChoiceField(
                        choices=models.Classes.objects.values_list('id','title'),
                        widget=widgets.SelectMultiple
                    )
                    
                    obj = FooForm({'cls_id':[1,2,3]})

    老师的models 代码:

from django.db import models

class Classes(models.Model):
    title = models.CharField(max_length=32)

    def __str__(self):
        return self.title

class Student(models.Model):
    name = models.CharField(max_length=32)
    email = models.CharField(max_length=32)
    age = models.IntegerField()
    cls = models.ForeignKey('Classes')


class Teacher(models.Model):
    tname = models.CharField(max_length=32)
    """
    10
    """
    c2t = models.ManyToManyField('Classes')
View Code

    老师的view后端代码:

def teacher_list(request):
    tea_list = models.Teacher.objects.all()
    return render(request,'teacher_list.html',{'tea_list':tea_list})

from django.forms import models as form_model

class TeacherForm(Form):
    tname = fields.CharField(min_length=2)
    # xx = form_model.ModelMultipleChoiceField(queryset=models.Classes.objects.all())
    # xx = form_model.ModelChoiceField(queryset=models.Classes.objects.all())

    xx = fields.MultipleChoiceField(
        # choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple
    )
    def __init__(self,*args,**kwargs):
        super(TeacherForm,self).__init__(*args,**kwargs)
        self.fields['xx'].choices = models.Classes.objects.values_list('id','title')

# obj = TeacherForm()
# 1. 找到所有字段
# 2. self.fields = {
#       tname: fields.CharField(min_length=2)
# }

def add_teacher(request):
    if request.method == "GET":
        obj = TeacherForm()
        return render(request,'add_teacher.html',{'obj':obj})
    else:
        obj = TeacherForm(request.POST)
        if obj.is_valid():
            xx = obj.cleaned_data.pop('xx')
            row = models.Teacher.objects.create(**obj.cleaned_data)
            row.c2t.add(*xx) # [1,2]
            return redirect('/teacher_list/')
        return render(request,'add_teacher.html',{'obj':obj})

def edit_teacher(request,nid):
    if request.method == "GET":
        row = models.Teacher.objects.filter(id=nid).first()
        class_ids = row.c2t.values_list('id')
        # print(class_ids)
        # id_list = []
        id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else []
        # obj = TeacherForm(initial={'tname':row.tname,'xx':[1,2,3]})
        obj = TeacherForm(initial={'tname':row.tname,'xx':id_list})
        return render(request,'edit_teacher.html',{'obj':obj})
View Code

    老师的teacher_list 前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>老师列表</h1>
    <div>
        <a href="/add_teacher/">添加</a>
    </div>
    <table border="1">
        <thead>
            <tr>
                <th>ID</th>
                <th>老师姓名</th>
                <th>任教班级</th>
                <th>编辑</th>
            </tr>
        </thead>
        <tbody>
            {% for row in tea_list %}
                <tr>
                    <td>{{ row.id }}</td>
                    <td>{{ row.tname }}</td>
                    <td>
                        {% for item in row.c2t.all %}
                            <span>{{ item }}</span>
                        {% endfor %}
                    </td>

                    <td>
                        <a href="/edit_teacher/{{ row.id }}/">编辑</a>
                    </td>
                </tr>
            {% endfor %}
        </tbody>
    </table>

</body>
</html>
View Code

    老师的add_teacher 前端代码:  

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form method="POST" action="/add_teacher/">
        {% csrf_token %}

        <p>
            姓名:{{ obj.tname }}
        </p>
        <p>
            班级:{{ obj.xx }}
        </p>
        <input type="submit" value="提交" />
    </form>
</body>
</html>
View Code

    老师的edit_teacher 前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>编辑老师</h1>
    <form>
        {{ obj.tname }}
        {{ obj.xx }}
    </form>
</body>
</html>
View Code

   Form组件的常用组件:

    CheckBox复选框的应用:

    test后端:

class TestForm(Form):
      t1 = fields.CharField(
          widget=widgets.Textarea(attrs={})
      )

      t2 = fields.CharField(   #单选的
          widget=widgets.CheckboxInput
      )
 
      t3 = fields.MultipleChoiceField(   #多选
          choices=[(1,'篮球'),(2,'足球'),(3,'溜溜球')],
          widget=widgets.CheckboxSelectMultiple
      )

      t4 = fields.ChoiceField(  #单选,选择互斥
          choices=[(1,'篮球'),(2,'足球'),(3,'溜溜球')],
          widget=widgets.RadioSelect
      )

      t5 = fields.FileField(   #上传文件
          widget=widgets.FileInput
      )

 

def test(request):
    obj = TestForm(initial={'t3':[2,3]})  #默认选中
    obj.is_valid()
    return render(request,'test.html',{'obj':obj})

    test前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    {{ obj.t2 }}
    {{ obj.t3 }}
    {{ obj.t4 }}
    {{ obj.t5 }}
</body>
</html>

   Form验证执行流程和钩子:

    is_valid方法的内部流程:

       is_valid的里面有一个return的返回,返回的有self.is_bound 和 self.errors。

        这个self.is_bound是只有True和False两个值,在初始化的时候,会进行校验,这个校验的值就是True 和 False。

        在self.errors里面有一个self.full_clean方法,这个方法就是去做验证的。这个验证就是循环Form自己的字段,

        然后去提交的数据进行校验。

          在self.full_clean里面,我们可以看到有cleaned_data,这就是我们的校验数据,这是一个字典类型。

          最后是clean_fields处理data的数据。      

self._clean_fields()  #做cleaned_data数据处理用
self._clean_form()   #做钩子用
self._post_clean()

    钩子:是自己定义的,当定义的钩子函数被调用是当每一个字段,自己的正则表达式,自己的函数后执行完,才会执行这个钩子函数。

       如果想用钩子函数,那么cleaned_data里是已经有值的啦。       

    样例:   

from django.core.exceptions import ValidationError
class TestForm(Form):
    user = fields.CharField(validators=[])
    pwd = fields.CharField()

    def clean_user(self):
        v = self.cleaned_data['user']
        if models.Student.objects.filter(name=v).count():
            raise ValidationError('用户名已经存在')
        return self.cleaned_data['user']

    def clean_pwd(self):
        return self.cleaned_data['pwd']

    def clean(self):   #钩子
        # user = self.cleaned_data.get('user')
        # email = self.cleaned_data.get('email')
        # if models.Student.objects.filter(user=user,email=email).count():
        #     raise ValidationError('用户名和邮箱联合已经存在')
        return self.cleaned_data
    # def _post_clean(self):
    #     """
    #     An internal hook for performing additional cleaning after form cleaning
    #     is complete. Used for model validation in model forms.
    #     """
    #     pass

   Form扩展:

      字段 = 默认的正则表达式。

      在默认的正则中,可以在加额外的正则,用validators=[]

    clean_字段 = 必须有返回值。

    clean():有返回值,用定义的返回值 cleaned_data = 返回值。

        没有返回值,用原来的值,cleaned_data = 原有的值。

 

    Form的验证流程:

      1. 写一个Form,用户提供大量的验证数据,先一个一个字段的去获取。

      2. 拿一个字段,做自己的正则,当自己的正则执行完,就执行自己的函数。

      3. 所有的字段执行完自己函数后,执行clean()方法。

   Form组件总结:    

    1. 使用
      class Foo:
        xx = xxxxxx() # 正则,插件

        def clean_xx():
          ...
        def clean():
          pass

    2. 页面展示
      obj = Foo()

      # 灵活
      <form>
        {{obj.xx}}
        {{obj.xx}}
        {{obj.xx}}
      </form>

      # 简单
      {{obj.as_p}}
      <ul>
        {{obj.as_ul}}
      </ul>
      <table>
        {{obj.as_table}}
      </table>

    3. 后台
      is_valid()
      clean_data
      errors

 

   参考blog:XXXXXXXX6144178.html

------- END -------

posted @ 2019-09-01 11:29  王先生是胖子  阅读(218)  评论(0编辑  收藏  举报