• 28 Jun 18 Django,FORM表单


    28 Jun 18

    一、内容回顾

    1. AJAX(前端向后端发送请求的方式之一)

       前端向后端发送请求的四种方式:

       a. 直接在浏览器地址栏输入URL访问--> GET

       b. 点击a标签跳转到指定页面       --> GET

       c. form表单                       --> GET/POST

       d. AJAX                           --> GET/POST

     

       注意:

       a. 单独的input没有办法向后端发送请求

       b. 以上四种方式相对独立;FORM按照FORM的方式,AJAX按照AJAX的方式(对于AJAX,后端只能取到data中的数据)

     

    2. jQuery版AJAX

       a. 一般请求

       $.ajax({

           url: "/index/",  // 往哪里发送请求

           type: "POST",  // 请求的方法

           data: {name:"张曌",age:16},  // 请求的数据

           success:function(data){

               // 请求被正常响应时自动执行的回调函数

               console.log(data)

           }

       })

     

       b. 提交携带文件类型的数据(BBS中用户上传头像时会用到)

       var formData = new FormData();  // 生成一个FormData对象

       var fileObj = $("#f1")[0].files[0]  // 得到用户选中的文件对象

      formData.append("f1", fileObj) // 向formData对象中添加键值对数据

       $.ajax({

           url: "/index/",  // 往哪里发送请求

           type: "POST",  // 请求的方法

           processData: false,  // 不让jQuery处理我的数据

           contentType: false,  // 不让jQuery设置请求头

           data: formData,

           success:function(data){

               // 请求被正常响应时自动执行的回调函数

               console.log(data)

           }

       })

     

       c. AJAX处理CSRF_TOKEN的三种方式

          1)手动把csrf_token的值取到,塞进请求的data中

          2)从cookie中取csrf_token,塞进请求头中

          3)在一个单独的JS文件中,给$.ajax统一设置一下请求头

     

       d. 注意: 如果发送的data中,数据不是简单的字符串或数字,需要用JSON.stringify()转换成JSON字符串

     

    3. 序列化(serializer):快速把ORM对象转换成JSON格式的数据

     

    4. 作业讲解(注册时的用户名查重,登陆成功后的跳转)

        # views.py

        from django.shortcuts import render, redirect

        from app01 import models

        from django.http import JsonResponse

        

        def register(request):

            return render(request, "register.html")

        

        def check_name(request):

            if request.method == "POST":

                ret = {"code": 0}

                username = request.POST.get("name")

                is_exist = models.User.objects.filter(name=username)

                if is_exist:

                    ret = {"code": 1, "errMsg": "用户名已存在!"}

                return JsonResponse(ret)

        

        def login(request):

            if request.method == "POST":

                ret = {"code": 0}

                name = request.POST.get("name")

                pwd = request.POST.get("pwd")

                ok = models.User.objects.filter(name=name, pwd=pwd)

                if not ok:

                   ret["code"] = 1

                   ret["data"] = "用户名或密码错误"

                else:

                    ret["data"] = "http://www.luffycity.com"

                return JsonResponse(ret)

                # 注意不能用return redirect("http://www.luffycity.com");redirect()这个响应浏览器可以解析,但AJAX不能解析

                # AJAX 只可以处理HttpResponse()或JsonResponse()这系列响应

            return render(request, "login.html")

            

        # register.html

        <body>

        <p>

            用户名:<input type="text" id="i1">

            <span class="error" id="s1"></span>

        </p>

        <p>

            密码:<input type="password" id="i2">

        </p>

        <p>

            <button id="b1">注册</button>

        </p>

        

        <script src="/static/jquery-3.3.1.min.js"></script>

        <script src="/static/setupAjax.js"></script>

    <script>

    {# input 只能用.on("input", function () {}这种形式#}

    {# 如果想失去光标时触发,用blur,可以直接.blur, 也可以.on("blur", function () {} #}

            $("#i1").on("input", function () {

                $("#s1").text("");

                {#每次触发input时,先清空error span中的数据,这样如果数据库中有用户名'zhangzhao','zhangzhao1'依旧可以作为新用户名被使用#}

                var value = $(this).val();

                $.ajax({

                    url: "/check_name/",

                    type: "POST",

                    {#这里如果是POST,后端必须用POST方法取数据#}

                    data: {name: value},

                   success:function (data) {

                       console.log(data);

                        if (data.code){

                           $("#s1").text(data.errMsg);

                        }

                    }

                })

            })

        </script>

        </body>

        

        # login.html

        <body>

        <p>

            用户名:<input type="text" id="i1">

            <span class="error" id="s1"></span>

        </p>

        <p>

            密码:<input type="password" id="i2">

        </p>

        <p>

            <button id="b1">登录</button>

        </p>

        

        <script src="/static/jquery-3.3.1.min.js"></script>

        <script src="/static/setupAjax.js"></script>

        <script>

           $("#b1").click(function () {

                var name = $("#i1").val();

                var pwd = $("#i2").val();

                $.ajax({

                    url: "/login/",

                    type: "POST",

                    data: {name: name, pwd: pwd},

                   success:function (data) {

                        if (!data.code){

                            location.href = data.data;

              {# location.href 等同于windows.location.href,是用js代码实现跳转的方式#}

                        }

                    }

                })

            })

        </script>

    </body>

     

    二、今日内容(form表单)

    http://www.cnblogs.com/liwenzhou/p/8747872.html

    1. form组件: 类似orm,一个form类对应一个form表单;可以帮助处理以下三方面需求

        a. HTML文件由form类的对象自己生成 (只能生成获取用户信息的那些input标签(不包含form,csrt_token, submit等))

        b. 对提交过来的数据做校验,返回错误提示信息

        c. 在页面中保留用户原来填写的信息 (自己手写form表达,用户之前的填写若想保留处理起来比较麻烦)

     

    2. form组件的用法

        a. 自定义一个form类

        b. 生成一个form类的实例对象

        c. 在前端页面

     

            <form action-"/reg2/" method ="post" novalidate>

                {% csrf_token %}

                {{ form_obj.as_p }}

                {{ form_obj.errors }}

                <input type="submit" value="注册">

            </form>

     

            form_obj.as_p --> 用p标签包裹每一个字段

            #1 as_p 中包括提示性的文本{{ form_obj.username.label }}、input标签{{ form_obj.username }}、响应的错误提示信息{{ form_obj.username.errors.0 }}

            #2 不推荐使用as_p,不够灵活; 可以使用以下形式,使后续处理(bootstrap等)更加便利

            

            # 推荐使用以下方式

            <div>

            <label for="">{{ form_obj.username.label }}</label>

            {{ form_obj.username }}

            <span class="error">{{ form_obj.username.errors.0 }}</span>

            # 根据索引,在errors列表中取第一个error

            </div>

     

            form_obj.errors  --> 包括全部字段的错误提示

     

        d. 在后端

            form_obj.is_valid()    --> 对数据做有效性校验

            form_obj.cleaned_data  --> 获取所有经过校验的数据;直接帮忙过滤掉csrf_token

     

        e. 不让浏览器帮忙校验

           <form action="/reg2/" method="post" novalidate>

     

        f. error_messages

           error_messages={

                "required":"不能为空",

                "invalid":"格式错误",  # 格式错误,一般用在email中

                "min_length":"用户名最短8位"

            }

     

    g. 密码

      from django import forms

           class LoginForm(forms.Form):

           ...

           pwd = forms.CharField(

               min_length=6,

               label="密码",

               widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)

               # render_value=True: 当提交的密码不符合要求时,原来的值还在

               # 一般来说,不推荐使用;防止该用户忘记之前的输入,提高用户体验

           )

     

        h. 单选

           gender = forms.fields.ChoiceField(

               choices=((1, "男"), (2, "女"), (3, "保密")),

               # choices=((value, 提示信息),()),

               label="性别",

               initial=3,  # 默认选中

               # 指定生成HTML标签的时候生成的是什么类型的

               widget=forms.widgets.RadioSelect  # radioSelect

               widget=forms.widgets.Select  # 单选Select

               widget=forms.widgets.CheckboxInput  # 单选checkbox

            )

     

        i. 多选

           hobby = forms.fields.MultipleChoiceField(

               choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),

               label="爱好",

               initial=[1, 3],  #为列表

               widget=forms.widgets.SelectMultiple  # 多选Select

               widget=forms.widgets.CheckboxSelectMultiple  # 多选checkbox

            )

     

        j. 关于choice的注意事项

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

           # 效果:不需重启,choice的选项根据数据库的数据实时更新

     

           from django.forms import Form

           from django.forms import widgets

           from django.forms import fields

     

           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'].choices = models.User.objects.all().values_list('id','caption')

     

    3. 推荐前后端都进行校验

       a. 推荐设置前端校验(如校验是否所有必须项都被filled),可以减少后端压力

       b. 但是,要以后端校验为主;一来,是对要写入数据库的数据做最终的校验,二来,当前端校验被跳过时(浏览器可禁用JS),后端校验为数据安全性提供保障

     

    4. 自定义校验的三种方式

       # 复习正则

       import re

       r = re.compile(r'^[^键盘膜s]+$')  # 不能包含键盘膜和空格

       print(r.match('alex键盘膜asd'))  # None

       # Django框架中用的是match匹配(从头)

       print(r.match('alex键盘膜'))  # None

       print(r.match('键盘膜asd'))  # None

       print(r.match('alex'))  # <_sre.SRE_Match object; span=(0, 4), match='alex'>

     

       1. 正则

       from django.forms import Form

       from django.forms import widgets

       from django.forms import fields

       from django.core.validators import RegexValidator

     

       class RegForm(forms.Form):

        username = forms.CharField(

            min_length=6,

            label="用户名",

            error_messages={

               "required": "不能为空",

               "min_length": "用户名最少6位"

            },

            validators=[RegexValidator(r'^[^键盘膜s]+$', "用户名不符合社会主义核心价值观!"), ]

     

       2. 自己写函数,注册到validations

       import re

       from django.forms import Form

       from django.forms import widgets

       from django.forms import fields

       from django.core.exceptions import ValidationError

     

       def zhangzhao(value):

           print(value)

           if "键盘膜" in value:

               raise ValidationError("不符合社会主义核心价值观!")

     

       class RegForm(forms.Form):

           username = forms.CharField(

           min_length=6,

           label="用户名",

           error_messages={

               "required": "不能为空",

               "min_length": "用户名最少6位"

            },

            validators=[zhangzhao, ]

     

       3. 钩子函数

       # 局部钩子函数

       import re

       from django.forms import Form

       from django.forms import widgets

       from django.forms import fields

       from django.core.exceptions import ValidationError

     

       class RegForm(forms.Form):

           username = forms.CharField(

           min_length=6,

           label="用户名",

           error_messages={

              "required": "不能为空",

               "min_length": "用户名最少6位"

           },

     

           def clean_username(self):

               value = self.cleaned_data.get("username")

               if "键盘膜" in value:

                   raise ValidationError("不符合社会主义核心价值观!")

                   # 要用django中的钩子,需要按照它的规则调用接口;此处必须raise ValidationError

               else:

                   return value

                   # 要用django中的钩子,需要按照它的规则调用接口;此处必须要有返回值

     

    5. 局部钩子和全部钩子(走源码)

       # 根据源码, 校验时,先走设置的校验,再走自定义校验(钩子)

     

       self.errors  --> self._errors = {} --> 用来存放错误信息

       self.cleaned_data = {}                    --> 用来存放通过校验的数据

     

       models.User.objects.create(**form_obj.cleaned_data)(清理数据后,添加到数据库)

       # 此种方式大大简化了实例化的代码量,以后会经常使用

     

       a. 局部钩子:局部钩子函数主要用来自定义校验某个字段,例如我需要校验用户输入的手机号是否满足一定规则

       b. 全局钩子:全局钩子函数主要用来校验所有的字段

     

       全局钩子应用(对比两次密码是否输入一致)

       # 用局部钩子解决不了,因为局部钩子在执行中只在本字段(re_pwd)内for循环,没有办法和其他字段(pwd)做对比

       class RegForm(forms.Form):

     

           pwd = forms.CharField(

               min_length=3,

               label="密码",

               error_messages={

                  "required": "不能为空",

                  "min_length": "密码最少3位"

               },

              widget=forms.widgets.PasswordInput(

                   attrs={'class': 'shs1'},

                  render_value=True

               )

           )

           re_pwd = forms.CharField(

               min_length=3,

               label="密码",

               error_messages={

                   "required": "不能为空",

                  "min_length": "密码最少3位"

               },

              widget=forms.widgets.PasswordInput(

                   attrs={'class': 'shs1'},

                  render_value=True

               )

           )

     

           def clean(self):

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

               re_pwd = self.cleaned_data.get("re_pwd")

     

               if pwd != re_pwd:

                  self.add_error("re_pwd", "两次输入的密码不一致!")

                   raise ValidationError("两次输入的密码不一致!")

                   # 要用django中的钩子,需要按照它的规则调用接口;此处必须raise ValidationError

               else:

                   return self.cleaned_data

                   # 要用django中的钩子,需要按照它的规则调用接口;此处必须要有返回值

  • 相关阅读:
    Centos7永久修改hostname
    centos静态绑定IP地址
    Salesforce 报表开发
    Salesforce 测试类的实践
    Salesforce Aura开发 Component组件开发实践
    Salesforce Aura 开发 Hello World开发实践
    PHP设计模式之工厂模式
    PHP设计模式之单例模式
    PHP截取带有汉字的字符串,将汉字按两个字节计算
    window下安装Apache+PHP
  • 原文地址:https://www.cnblogs.com/zhangyaqian/p/py20180628.html
Copyright © 2020-2023  润新知