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.Classes.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中的钩子,需要按照它的规则调用接口;此处必须要有返回值

猜你喜欢

转载自www.cnblogs.com/zhangyaqian/p/py20180628.html
今日推荐