• Django之Form组件


    1.内容回顾

    1)Django请求生命周期

    HTTP请求->WSGI服务器(WEB服务网关接口)->中间件->url(路由系统)->view(匹配视图函数)->views从model取数据->从前端拿模板->模板渲染给用户返回一个大的字符串(网页内容)    

    2)Session是什么?

    Session:是保存在服务端的数据,当用户登录时,服务端生成随机字符串给客户端的浏览器,浏览器写到cookie里,服务器端把随机字符串保存起来,服务端的随机字符串和客户端的cookie里的随机字符串一一对应。

    3)XSS是什么?

    XSS:跨站脚本攻击
            举例:评论(while循环,alert),
            防止XSS(1.客户端提交什么数据,以安全的形式查看,当作字符串让用户访问,2.用户提交时过滤关键字,如script)

    4)CSRF-POST(银行的例子) 两个网站

    CSRF:跨站请求伪造(POST请求)
            不仅发送数据,还要发送随机字符串(上一次POST请求获取到的)

    2.今日内容:

     Form组件

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

    Form作用有两个1.用户提交数据进行校验;2.保留上次输入内容

    提交数据方式:1.Form提交;2.Ajax提交

    1. 用户提交数据进行校验

    - Form提交(刷新,失去上次内容)
    a. LoginForm(Form)
    字段名 = xxxx.xxField() # 本质就是验证规则,正则表达式
    字段名 = xxxx.xxField() # 本质验证规则,正则表达式
    字段名 = xxxx.xxField() # 本质验证规则,正则表达式
    字段名 = xxxx.xxField() # 本质验证规则,正则表达式
    b. obj = LoginForm(用户提交的数据,request.POST)
    
    c. result = obj.is_valid() 进行校验 print(result)返回True 或者False
    
    d. obj.cleaned_data:是一个字典,如果校验成功了,拿到正确的值
    
    e. obj.errors:是一个对象,包含所有的错误信息

    内部原理

    def login(request):
        if request.method == 'GET':
            return render(request, 'login.html')
        else:
            obj = LoginForm(request.POST)
            #is_valid
            """
            获取当前类所有的对象
            1.LoginForm实例化时,self.fields中
            self.fields={
                'user':正则表达式,
                'pwd':正则表达式,
            }
            2.循环self.fields,
                flag = True验证通过(用户名密码。。都输入正确)
                errors
                cleaned_data
                for k,v in self.fields.items():
                    k是user,pwd
                    v是:正则表达式
                    input_value = request.POST.get(k):前端输入的值,写几个字段,就取谁
                正则表达式和input_value进行正则匹配(这里用的是match)
                    flag = False
                return flag
            """
            if obj.is_valid():#如果正确
                print(obj.cleaned_data)#字典类型
                return redirect('http://www.baidu.com')
            else:
                print(obj.errors)
            return render(request, 'login.html', {'obj':obj})
    Form提交登录验证代码login函数
    def login(request):
                            if request.method == 'GET':
                                return render(request,'login.html')
                            else:
                                obj = LoginForm(request.POST)
                                # is_valid
                                """
                                1. LoginForm实例化时,
                                    self.fields={
                                        'user': 正则表达式
                                        'pwd': 正则表达式
                                    }
                                2. 循环self.fields
                                      flag = True
                                      errors
                                      cleaned_data
                                      for k,v in self.fields.items():
                                        # 1. user,正则表达式
                                        input_value = request.POST.get(k)
                                        正则表达式和input_value
                                        flag = False
                                      return flag
                               """
                                if obj.is_valid():
                                    print(obj.cleaned_data)
                                else:
                                    print(obj.errors)
                                return render(request,'login.html')
    View Code
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    
        <h1>用户登录</h1>
        <form id="f1" action="/login/" method="POST">
            {% csrf_token %}
            <p>
                用户名:<input type="text" name="user" />{{ obj.errors.user.0 }}
            </p>
            <p>
                密码:<input type="password" name="pwd" />{{ obj.errors.pwd.0 }}
            </p>
            <input type="submit" value="提交">
            <a onclick="submitForm();">提交</a>    #onclick绑定事件
        </form>
        <script src="/static/jquery-1.12.4.js"></script>
        <script>
            function submitForm(){
                $('.c1').remove();    #把以前的错误信息都清除掉
                $.ajax({
                    url:'/ajax_login/',    
                    type:'POST',        #提交方式
                    data:$('#f1').serialize(),    #找到f1标签,jQuery ajax - serialize() 方法,serialize() 方法通过序列化表单值,创建 URL 编码文本字符串。
                    datatype:"JSON",                    #serialize()拼接字符串,  user=alex&pwd=456&csrf_token=bing
                    success:function(arg){
                        console.log(arg);
                        if (arg.status){
    
                        }else{
                            $.each(arg.msg,function (index,value) {    循环把所有错误信息输出index,value(也就是key和value )
                                console.log(index,value);
                                var tag = document.createElement('span');    #创建节点:
                                tag.innerHTML = value[0];        #获取第一个错误信息,innerHTML 给节点添加html代码:
                                tag.className = 'c1';
                                $('#f1').find('input[name="'+ index +'"]').after(tag);    #在span标签的后面
                                //'  input[name="  ' + index + '  "]  '==input[name=user]
                            })
                        }
                    }
                })
            }
        </script>
    
    </body>
    </html>
    前端login.html代码

    前端涉及知识点:

    1) createElement(标签名) :创建一个指定名称的元素。

    例:var tag=document.createElement(“input")
    tag.setAttribute('type','text');

    2)data:$('#f1').serialize()  serialize() 方法可以操作已选取个别表单元素的 jQuery 对象,比如 <input>, <textarea> 以及 <select>。

    不过,选择 <form> 标签本身进行序列化一般更容易些:
    打包user,pwd,csrf_token,拼接字符串

    咱们写的字典也会变成字符串
    请求体:'user=alex&pwd=456&csrf_token=bing'

    注意:render的本质还是返回HttpResponse(字符串)

    return render():
    def render(request, template_name, context=None, content_type=None, status=None, using=None):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
    """
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

    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。
    from django.shortcuts import render,HttpResponse,redirect
    from django.forms import Form
    from django.forms import fields
    
    class Login(Form):
                                  #from验证规则 用户名 6-10字符  required不能为空
        name=fields.CharField(max_length=10,
                              min_length=6,
                              required=True,
                               error_messages={
                                   'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                                   'min_length':'太短了',
                                    'max_length': "太长了",
                                               }
    
                              )
                                                                        # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
        pwd= fields.CharField(min_length=3,
                              required=True,
                              error_messages={
                                  'required': '密码不能为空',  # error_messages参数 自定义错误信息
                                  'min_length': '太短了',
                                  'max_length': "太长了",
                              }
    
    
    
                              )
    
    
    def index(request):
        if request.method=='GET':
            return render(request,'login.html')
        else:
            obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一起
            res=obj.is_valid()         #自动校验  给出结果 True 或者 False
            if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
                print(obj.cleaned_data)
                return redirect('http://www.baidu.com')                 #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
            else:
               return  render(request,'login.html',{'obj':obj})
    form表单提交验证(form表单(会发起 get)提交刷新失去上次内容)

    2.- Ajax提交(不刷新,上次内容自动保留)

    PS: Ajax提交(页面不刷新,数据不清空) > Form提交(Form表单一刷新,页面刷新,输入的数据都会清空)    总结:

    class Foo(Form):
                字段 = 正则表达式  :是否可以为空,最长,最短
                字段 = 自定义正则表达式
                       1. 常用
                            charField 
      ... 定义正则表达式 参数: 验证:(本质上都是正则表达式) required error_messages 生成HTML标签: widget
    =widgets.Select, ******** 用于指定生成怎样的HTML,select,text,input/. label='用户名', # obj.t1.label disabled=False, # 是否可以编辑 label_suffix='--->', # Label内容后缀 initial='666', # 无用,猜测有问题应该在input框中显示默认值 help_text='。。。。。。', # 提供帮助信息

    - 生成HTML标签
    - 保留上次输入内容

    IntegerField继承Field
    CharField 继承Field
    EmailField继承 CharField
    URLField继承CharField

    t4 = fields.URLField()
    t5 = fields.SlugField() #字母数字下划线,内部也是正则表达式
    t6 = fields.GenericIPAddressField()
    t7 = fields.DateTimeField()
    t8 = fields.DateField()
    def ajax_login(request):
        # request.POST.get()
        import json
        ret = {'status':True,'msg':None}
        obj = LoginForm(request.POST)
        if obj.is_valid():
            print(obj.cleaned_data)#{'user': 'alex', 'pwd': 'alexalexalexalexalex'}
        else:
            print(obj.errors)#obj.errors,是一个 对象(无序列表)
            ret['status'] = True
            ret['msg'] = obj.errors
            # error = json.dumps(obj.errors)
            # print(error)
        v = json.dumps(ret)
        print(v)#输入错误:输出{"status": true, "msg": {"user": ["This field is required."], "pwd": ["This field is required."]}}
        #输入正确:{"status": true, "msg": null}
        return HttpResponse(v)#提交错误时,<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li><li>pwd<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
    #ajax提交
    # {'user': 'alex', 'pwd': 'alexalexalexalexalex'}
        # return render()
    AJAX提交方式(ajax_login函数)
        <script src="/static/jquery-1.12.4.js"></script>
        <script>
            function submitForm(){
                $('.c1').remove();
                $.ajax({
                    url:'/ajax_login/',
                    type:'POST',
                    data:$('#f1').serialize(),
                    datatype:"JSON",
                    success:function(arg){
                        console.log(arg);
                        if (arg.status){
    
                        }else{
                            $.each(arg.msg,function (index,value) {
                                console.log(index,value);
                                var tag = document.createElement('span');
                                tag.innerHTML = value[0];
                                tag.className = 'c1';
                                $('#f1').find('input[name="'+ index +'"]').after(tag);
                            })
                        }
                    }
                })
            }
        </script>
    前端jQuery+AJAX代码

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

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>ajx提交</title>
    </head>
    <body>
    <form method="post" action="/aja_login/" id="f1">
      {%csrf_token%}
    <p>用户:<input type="text" name="name"></p>
    <p>密码:<input type="password" name="pwd"></p>
    <p><input type="button" onclick="Ajxform()" value="aja提交"></p>
    </form>
    </body>
    <script src="/static/zhanggen.js"></script>
    <script> function Ajxform(){
        $('.c1').remove()
        $.ajax({
            url:'/alogin/',
            type:'POST',
            dataType:'JSON',
            data:$('#f1').serialize(),
            success:function (args) {
                if (args.status){ }
                else{
    {#                {status: false, msg: Object}#}
    {#                console.log(args);#}
    {#                Jquery循环服务端 传过来的 错误信息对象#}
                    $.each(args.msg,function (index,value) {
                        console.log(index,value);
    {#                 index----> name ["太短了"]#}
    {#                 value-----pwd["密码不能为空"]#}
                        var tag=document.createElement('span');
                        tag.innerHTML= value[0];
                        tag.className='c1';
                        console.log(index);
    {#                    寻找input下 属性为 name 和pwd的标签(字符串拼接) 在他们后半加 上tag标签也就是错误 信息 #}
                        $('#f1').find('input[name="'+ index +'"]').after(tag)
    
                    })
                }
    
                   }})}
    </script>
    </html>
    View Code

    Views

    from django.shortcuts import render,HttpResponse,redirect
    from django.forms import Form
    from django.forms import fields
    import json
    class Login(Form):
                                  #from验证规则 用户名 6-10字符  required不能为空
        name=fields.CharField(max_length=10,
                              min_length=6,
                              required=True,
                             error_messages={
                                   'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                                   'min_length':'太短了',
                                    'max_length': "太长了",
                                               }
    
                              )
                                                                        # z注意name 必须和 from表单提交的一致,否则二者怎么对比校验呢
        pwd= fields.CharField(min_length=3,
                              required=True,
                              error_messages={
                                  'required': '密码不能为空',  # error_messages参数 自定义错误信息
                                  'min_length': '太短了',
                                  'max_length': "太长了",})
    
    
    
    def agx_login(request):
        ret={'status':True,'msg':None}
        if request.method=='GET':
           return render(request,'ajalogin.html')
        else:
            obj=Login(request.POST)
            ret['status']=False
            ret['msg']=obj.errors
            return HttpResponse(json.dumps(ret))
    View Code

    Form组件之常用字段和参数

    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内容后缀
    Field

    动态生成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的数据'>然后后端再返回客户端就可以看到用户输入的值了!

    from django.shortcuts import render,HttpResponse,redirect
    from django.forms import Form
    from django.forms import fields
    import json
    class Login(Form):
                                  #Form验证规则 用户名 6-10字符  required不能为空
        name=fields.CharField(max_length=10,
                              min_length=6,
                              required=True,
                             error_messages={
                                   'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                                   'min_length':'太短了',
                                    'max_length': "太长了",
                                               }
    
                              )
                                                                        # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
        pwd= fields.CharField(min_length=3,
                              required=True,
                              error_messages={
                                  'required': '密码不能为空',  # error_messages参数 自定义错误信息
                                  'min_length': '太短了',
                                  'max_length': "太长了",})
    
    
    def index(request):
        ret={'status':True,'msg':None}
        if request.method=='GET':
            obj=Login()                 #自动生成空白的input标签 发送给客户端)
            return render(request,'login.html',{'obj':obj})
        else:
            obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一
            res=obj.is_valid()         #自动生成空白的input标签 发送
            if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
                return HttpResponse('OK') #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
            else:
               return render(request,'login.html',{'obj':obj})
    views.py
    <!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>
    前端页面

     方式一(推荐):

    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')

    老师管理:

    老师列表:

    class TeacherForm(Form):
        tname = fields.CharField(min_length=2)
        xx = fields.MultipleChoiceField(  #或者ChoiceField
            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'].widget.choices = models.Classes.objects.values_list('id','title')
    每实例化一次取一次值(也就是每进行一次操作从数据库取一次数据)
    obj = TeacherForm() 实例化成对象
    1.找到所有字段
    2.self.fields={
        tname:fields.CharField(min_length=2)
    }
    解决TeacherForm类的BUG
    def teacher_list(request):
        tea_list = models.Teacher.objects.all()
        print(tea_list)#<QuerySet [<Teacher: Teacher object>, <Teacher: Teacher object>, <Teacher: Teacher object>, <Teacher: Teacher object>]>
        # print(tea_list.tname, tea_list.age, tea_list.title)
        return render(request,'teacher_list.html',{'tea_list':tea_list})
    teacher_list
    class TeacherForm(Form):
        tname = fields.CharField(min_length=2)
        xx = fields.MultipleChoiceField(  #或者ChoiceField:单选
            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'].widget.choices = models.Classes.objects.values_list('id','title')
    # obj = TeacherForm()
    #1.找到所有字段
    #2.self.fields={
    #     tname:fields.CharField(min_length=2)
    # }
    TeacherForm类
    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():
                # print(obj.cleaned_data)#{'tname': '方少伟', 'xx': ['2', '3']}
                xx = obj.cleaned_data.pop('xx')#把不必要的东西删掉
                row = models.Teacher.objects.create(**obj.cleaned_data)
                print(row)#
                row.c2t.add(*xx)#添加一个字典到第三张表
                return redirect('/teacher_list/')
            # print('okokokokok')
            return render(request,'add_teacher.html',{'obj':obj})
    add_teacher
    修复Bug,刷新无法动态显示数据库内容:
        方式一:
            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())
    
        方式二:
            class TeacherForm(Form):
                tname = fields.CharField(min_length=2)
    
                xx = fields.MultipleChoiceField(
                    widget=widgets.SelectMultiple
                )
                def __init__(self,*args,**kwargs):
                    super(TeacherForm,self).__init__(*args,**kwargs)
                    self.fields['xx'].widget.choices = models.Classes.objects.values_list('id','title')
                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
                        )

    方式二:适用于写比较简单的程序,不推荐用这个,必须跟model结合起来用,耦合性太强,依赖数据,依赖__str__方法,程序越写越大,数据库可能会单独拆分出去,有人专门写数据库操作,客户端发HTTP请求,服务端会把结果返回给你,客户端拿到的是JSON对象,那就不是一个models对象了。

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

    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())
    定义TeacherForm类

    添加老师会出现如下现象:select框全是Classes对象

    解决方式:

    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)
        c2t = models.ManyToManyField('Classes')
    models.py

    在班级类添加一个__str__方法:

        def __str__(self):
            return self.title

    然后select框恢复正常:

     Form组件:生成常用标签

    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.MultipleChoiceField(  #这里也可以写成ChoiceField
          choices = [(1,'游泳'),(2,'写代码'),[3,'旅游']],
      widget=widgets.RadioSelect #单选
      )
      
      t5 = fields.FileField(  
      idget=widgets.FileInput
      )
    def test(request):
        obj = TestForm()
        return render(request,'test.html',{'obj':obj})

      

    is_valid-->self.errors错误信息(def errors(self))-->self.full_clean()做验证-->def full_clean(self)-->self._clean_fields(self):
    def is_valid(self):
        """
        Returns True if the form has no errors. Otherwise, False. If errors are
        being ignored, returns False.
        """
        return self.is_bound and not self.errors
        初始化时,data(True,要进行验证)   initial(False)  等于True或者False
        
    @property
    def errors(self):
        "Returns an ErrorDict for the data provided for the form"
        if self._errors is None:
            self.full_clean()  做验证
        return self._errors        
        
      def full_clean(self):
            """
            Cleans all of self.data and populates self._errors and
            self.cleaned_data.
            """
            self._errors = ErrorDict()
            if not self.is_bound:  # Stop further processing.
                return
            self.cleaned_data = {}空字典  字典格式,做校验时填充数据
            # If the form is permitted to be empty, and none of the form data has
            # changed from the initial data, short circuit any validation.
            if self.empty_permitted and not self.has_changed():
                return
    
            self._clean_fields()  字段
            self._clean_form()
            self._post_clean()
            
        def _clean_fields(self): self.fields是一个字典
            for name, field in self.fields.items():  循环自己的字段
                # value_from_datadict() gets the data from the data dictionaries.
                # Each widget type knows how to retrieve its own data, because some
                # widgets split data over several HTML fields.
                if field.disabled:
                    value = self.get_initial_for_field(field, name) 取值:request.POST.get('')
                else: field本质是正则表达式,根据正则表达式和输入的数据进行校验
                    value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
                try:
                    if isinstance(field, FileField):
                        initial = self.get_initial_for_field(field, name)
                        value = field.clean(value, initial)    #根据字段的正则表达式和输入的数据进行校验
                    else:
                        value = field.clean(value)  
                    self.cleaned_data[name] = value 如果验证通过,self.cleaned_data包含了用户提交过来并且验证成功的值
                    if hasattr(self, 'clean_%s' % name):  self代指的就是那个TestForm对象    name就是字段名   clean_字段名
                        value = getattr(self, 'clean_%s' % name)()    #clean开头,name结尾的字段,  加个括号执行函数
                        self.cleaned_data[name] = value
                except ValidationError as e:#抛出异常
                    self.add_error(name, e) #加到错误信息
    hasattr:判断object中有没有一个name字符串对应的方法或属性,hasattr先判断有没有
    getattr(object, name, default=None)  
    
    每一个字段来,先执行正则表达式,再执行函数
    如果正则表达式没有通过,函数不会执行
    
    return False
    return True
    
    target:提交之后的动作
    
    from django.core.exceptions import ValidationError
    class TestForm(Form):
        user = fields.CharField(validators=[])#1
        pwd = fields.CharField()#2
    
        def clean_user(self):#3
            v = self.cleaned_data['user']
            if models.Student.objects.filter(name=v).count(): #如果验证数据成功了
                # raise ValueError('用户名已经存在')
                raise ValidationError('用户名已经存在',code='invalid')    #code='invalid' / 'required' 'max_length' 'min_length'
            return self.cleaned_data['user']#必须有返回值,否则返回None
        def clean_pwd(self):#4
            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 self.cleaned_data #
            return self.cleaned_data
            
    def _clean_form(self):
        try:
            cleaned_data = self.clean() #如果返回None则不会重新赋值
        except ValidationError as e:#如果出错执行该语句
            self.add_error(None, e)  
        else:
            if cleaned_data is not None: #如果没出错执行该语句
                self.cleaned_data = cleaned_data  #重新赋值
        def clean(self): #这个功能可以自定制
            """
            Hook for doing any extra form-wide cleaning after Field.clean() has been
            called on every field. Any ValidationError raised by this method will
            not be associated with a particular field; it will have a special-case
            association with the field named '__all__'.
            """  Hook:钩子
            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
    # 执行顺序:1-3-2-4
    Form验证源码分析
    c. 扩展
        - is_valid
            - 字段 = 默认正则表达式
                 - 额外的正则
                    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开头')],
                        )
            - clean_字段,必须返回值
            - clean()  
                有返回值:cleaned_data = 返回值
                无返回值:cleaned_data = 原来的值
    扩展知识
    1. 使用
        class Foo:
            xx = xxxxxx() # 正则,插件
            
            
            def clean_xx():
                ..
                
                
            def clean():
                pass
                
    2. 页面展示
        obj = Foo()
        obj = Foo(init..)
        
        # 灵活
            <form>
                {{obj.xx}}
                {{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    可以被JSON序列化
    思考题:
    class Input:
    def init(self,attrs,value):
        self.attrs
    
    def __str__():
        return <input values='x'  />
    Form总结

    文件上传功能

    Form文件上传:

    from django.core.files.uploadedfile import InMemoryUploadedFile
    def f1(request):
        if request.method == "GET":
            return render(request,'f1.html')
        else:
            # print(request.FILES)#输出对象<MultiValueDict: {}>   #<MultiValueDict: {'fafafa': [<InMemoryUploadedFile: QQ图片20170210015937.gif (image/gif)>]}>
            # print(request.FILES.get('fafafa'))#输出对象 拿到字符串,而getlist拿到的是列表,文件内容  QQ图片20170210015937.gif
            # print(request.FILES.getlist('fafafa'))#输出对象InMemoryUploadedFile [<InMemoryUploadedFile: QQ图片20170210015937.gif (image/gif)>]
            file_obj = request.FILES.get('fafafa')
            # print(type(file_obj))#<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
            # print(file_obj.name)
            # print(file_obj.size)
            # f = open(file_obj.name,'wb')
            f = open(os.path.join('static',file_obj.name),'wb') 
            for chunk in file_obj.chunks():
                f.write(chunk)
            f.close()
            return render(request,'f1.html')
    View Code
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
    
        <form method="POST" action="/f1/" enctype="multipart/form-data">
            {% csrf_token %}
            <input type="file" name="fafafa">
            <input type="submit" value="提交">
        </form>
    
    </body>
    </html>
    前端页面

    注意事项:

    <form method="POST" action="/f2/" enctype="multipart/form-data">
        {% csrf_token %}
    
        {{ obj.user }}
        {{ obj.fafafa }}
    
        <input type="submit" value="提交">
    </form>
    
    form表单栏必须加这一句:enctype="multipart/form-data",否则上传不成功
    View Code

      

  • 相关阅读:
    Java 数组的浅拷贝和深拷贝
    Java 传递可变参数和方法重载
    Java 数组排序
    Java 一维数组作为参数和返回值
    Java 运算符及优先级
    MySQL 由 5.7 升级为 8.0 之后,Laravel 的配置改动
    Lavarel
    Laravel框架中Blade模板的用法
    php-fpm 配置文件检测
    Laravel Blade 模板 @section/endsection 与 @section/show, @yield 的区别
  • 原文地址:https://www.cnblogs.com/bingabcd/p/7119207.html
Copyright © 2020-2023  润新知