zoukankan      html  css  js  c++  java
  • 基于forms组件和Ajax实现注册功能

    一、基于forms组件的注册页面设计

    1、运用forms组件的校验字段功能实现用户注册 

    views.py:    (在钩子中代码解耦,将form放在cnblog/blog/Myforms.py中)

    from django import forms
    from django.forms import widgets
    
    class UserForm(forms.Form):
        user = forms.CharField(max_length=32,
                               label="用户名",
                               widget=widgets.TextInput(attrs={"class": "form-control"},)
                               )
        pwd = forms.CharField(max_length=32,
                              label="密码",
                              widget=widgets.PasswordInput(attrs={"class": "form-control"}, )
                              )
        re_pwd = forms.CharField(max_length=32,
                                 label="确认密码",
                                 widget=widgets.PasswordInput(attrs={"class": "form-control"},)
                                 )
        email = forms.EmailField(max_length=32,
                                 label="邮箱",
                                 widget=widgets.EmailInput(attrs={"class": "form-control"},)
                                 )
    
    def register(request):
        form = UserForm()   # 实例化form对象
        return render(request, "register.html", {"form": form})  # 注意要传入的是一个字典
    

      注意:

      (1)在视图层引入widgets模块,配置修改forms类参数。在这里添加了class="form-control"的bootstrap样式。添加了label标签。

      (2)这里register视图,实例化的form对象是未绑定数据的form表单。将这个form对象传入render中,注意传入的格式必须是字典的形式。

    2、forms表单渲染标签生成注册页面

    register.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css">
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form action="">
                    {% csrf_token %}
                    {% for field in form %}
                        {# 一个个获取字段对象 #}
                        <div class="form-group">
                            <label for="user">{{ field.label }}</label>
                            {{ field }}
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avator">头像</label>
                        <input type="file">
                    </div>
                    <input type="button" class="btn btn-default login-btn" value="提交"><span class="error"></span>
                </form>
            </div>
        </div>
    </div>
    <script src="/static/js/jquery-3.3.1.js"></script>
    </body>
    </html>
    

      显示效果如下所示:

      

    二、博客注册页面头像

     1、默认头像

    register.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>注册页面</title>
        <link rel="stylesheet" href="/static/blog/bootstrap-3.3.7/css/bootstrap.css">
        <style>
            #avator_img {
                margin-left: 20px;
            }
            #avator {
                display: none;
            }
        </style>
    </head>
    <body>
    <h3>注册页面</h3>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-lg-offset-3">
                <form action="">
                    {% csrf_token %}
                    {% for field in form %}
                        {# 一个个获取字段对象 #}
                        <div class="form-group">
                            <label for="{{ field.auto_id }}">{{ field.label }}</label>
                            {{ field }}
                        </div>
                    {% endfor %}
                    <div class="form-group">
                        <label for="avatar">
                            头像
                            <img  id="avatar_img" width="60" height="60" src="/static/blog/img/default.png" alt="">
                        </label>
                        <input type="file" id="avatar">
                    </div>
                    <input type="button" class="btn btn-default reg-btn" value="提交"><span class="error"></span>
                </form>
            </div>
        </div>
    </div>
    <script src="/static/js/jquery-3.3.1.js"></script>
    </body>
    </html>
    

      显示效果:

      

    注意:

    (1)添加头像上传和默认头像时,需要优化显示效果,点击默认头像,显示文件上传框图。这里应用的方法是巧妙使用label标签,将img标签嵌套在label标签中,设置<label for="">中的值与input标签的id相同。实现点击label标签就会默认点击到了input标签。设置完后,隐藏input标签的显示。

    (2)<label for="{{ field.auto_id }}"></label>:如果在循环user,filed.auto_id,返回的就是user_id。

      

    2、头像预览功能

      首先需要了解jquery事件的change()方法:当元素的值发生改变时,会发生 change 事件;当用于 select 元素时,change 事件会在选择某个选项时发生。当用于 text field 或 text area 时,该事件会在元素失去焦点时发生。

    <script>
        $("#avatar").change(function () {   // change方法,当输入域发生变化时改变其颜色(在这里是上传图片时触发)
            alert(123);
        })
    </script>
    

      上传图片:

      

      获取input标签用户选中的文件对象:

      

    (1)获取用户选中的文件对象

    // 获取用户选中的文件对象
    var file_obj = $(this)[0].files[0];

    (2)获取文件对象的路径

    <script>
        // 头像预览
        $("#avatar").change(function () {
            // 获取用户选中的文件对象
            var file_obj = $(this)[0].files[0];
            // 获取文件对象路径
            var reader = new FileReader();
            reader.readAsDataURL(file_obj);
    
            // reader.result;   // 读url的结果保存在result中
    </script>
    

      注意:readerAsDataURL()阅读文件的路径。读完url的结果保存在reader.result中。

    (3)修改img的src属性,src=文件对象的路径

    <script>
        // 头像预览
        $("#avatar").change(function () {   // change方法,当输入域发生变化时改变其颜色(在这里是上传图片时触发)
            // 获取用户选中的文件对象
            var file_obj = $(this)[0].files[0];
            // 获取文件对象路径
            var reader = new FileReader();
            reader.readAsDataURL(file_obj);
    
            // reader.result;   // 读url的结果保存在result中
            reader.onload=function () {  //
                // 修改img的src属性,src=文件对象的路径
                $("#avatar_img").attr("src", reader.result)   // 等事件完成之后再执行
            };
        })
    </script>
    

      显示效果:

      

      注意:

    (1)window.onload()等DOM执行完后,再来执行内部的代码;

    (2)reader.readAsDataURL();这个读取的时间比较长,同时,这个只是其中一个线程,如果直接执行

    $("#avatar_img").attr("src", reader.result)  
    

      这个语句会与readAsDataURL方法并发执行,由于前面方法还没有完成,reader.result还只是一个空值。

    (3)因此需要做一个load事件,等readAsDataURL读取完后再执行

    $("#avatar_img").attr("src", reader.result)    

    三、错误信息展示——基于Ajax提交formdata数据

    1、在register.html中编写注册提交事件(基于Ajax提交数据)

    // 基于Ajax提交数据
    $(".reg-btn").click(function () {
    
        var formdata =  new FormData();
        formdata.append("user", $("#id_user").val());
        formdata.append("pwd", $("#id_pwd").val());
        formdata.append("re_pwd", $("#id_re_pwd").val());
        formdata.append("email", $("#id_email").val());
        formdata.append("avatar", $("#avatar")[0].files[0]);  // 拿到用户传递的文件对象
        formdata.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());
    
        $.ajax({
            url: "",
            type: "post",
            contentType: false,
            processData: false,
            data: formdata,  // 将formdata对象放在data里面发送
            success:function (data) {
                console.log(data)
            }
        })

    注意:

      1)给注册按钮绑定一个click事件;

      2)由于上传的数据中包含一个图片文件,类型必须换为multipart/form-data格式,不再使用form-urlencoded 方式提交数据;

      3)构建的formdata对象:

    var formdata =  new FormData();
    

      4)给对象添加键值:

    formdata.append("user", $("#id_user").val());
    

      5)添加文件对象:

    formdata.append("avatar", $("#avatar")[0].files[0]);  // 拿到用户传递的文件对象
    

      $("#avatar")[0]拿到的是DOM对象,$("#avatar")[0].files[0]得到用户选中的文件对象(jquery取上传文件固定语法 :$("#avatar")[0].files[0]

      6)基于Ajax文件上传,传formdata数据一定要写下面这两个参数:

    contentType:false,   // 在ajax这一层不做任何编码(不设置内容类型)
    processData:false,   // 在ajax这一层不做预处理(不处理数据)
    

      7)要避免forbidden报错,需要添加csrf_token,查看模板中写入的{% csrf_token %}在页面中显示:

      

      然后,添加csrf_token键值:

    formdata.append("csrfmiddlewaretoken", $("[name='csrfmiddlewaretoken']").val());

    2、在视图中处理Ajax请求

    def register(request):
        if request.is_ajax():  # 也可以使用  if request.method == "POST":
            # 如果是一个Ajax请求
            print(request.POST)   # 所有提交的数据
            form = UserForm(request.POST)  # 创建UserForm对象,传入当前提交的字典
            response = {"user": None, "msg": None}
    
            if form.is_valid():  # form.is_valid是帮忙校验返回布尔值的,true或false(所有都通过才返回true)
                # 类定义的字段全符合要求,返回true
                response["user"] = form.cleaned_data.get("user")
    
            else:
                # 包含错误信息返回false
                print(form.cleaned_data)  # 字段值符合要求的放在cleaned_data
                print(form.errors)   # 字段不符合要求的对应的键作为键,错误信息作为值
                response["msg"] = form.errors
    
            return JsonResponse(response)
    
        form = UserForm()   # 实例化form对象
        return render(request, "register.html", {"form": form})  # 注入要传入的是一个字典

     在视图函数register中处理Ajax提交的请求需要注意的是:

      1)formdata提交给register处理,即是post请求,又是ajax请求,因此可以用两种方式来获取;

      2)form.is_valid是帮忙校验返回布尔值的,true或false(所有都通过才返回true);

    展示效果如下所示:

      

    3、基于Ajax提交formdata数据的优化

    // 基于Ajax提交数据
    $(".reg-btn").click(function () {
        var formdata =  new FormData();
        var request_data = $("#form").serializeArray();
        $.each(request_data, function (index, data) {
            // 依次循环,每次的操作
            formdata.append(data.name, data.value)
        });
        formdata.append("avatar", $("#avatar")[0].files[0]);  // 拿到用户传递的文件对象
    
        $.ajax({
            url: "",
            type: "post",
            contentType: false,
            processData: false,
            data: formdata,
            success:function (data) {
                console.log(data)
            }
        })
    })

    控制台输出如下:

       

    注意:

    (1)jQuery ajax - serializeArray() 方法

      serializeArray() 方法通过序列化表单值来创建对象数组(名称和值)。可以选择一个或多个表单元素(比如 input 及/或 textarea),或者 form 元素本身。

      在register.html中给form元素添加id="form"。在登录按钮的点击事件中添加:

    var ret = $("#form").serializeArray();
    console.log(ret);

      浏览器控制台输出如下:

      

      打印出一个列表,列表中保存一个个object类型,每个object类型都有name和value.

    (2)jqeury的遍历方法:.each()

      each() 方法为每个匹配元素规定运行的函数。返回 false 可用于及早停止循环。

      由于serializeArray()返回的是一个数组,因此这里each处理的request_data是一个数组。each方法会对数组中子元素的逐个进行fn函数调用,直至调用某个子元素返回的结果为false

      $.each(request_data, function (index, data) {}在这里index表示数组当前下标,data则表示数组当前元素。

    4、基于Ajax在注册页面显示错误信息

    (1) 在register.html中添加注册失败信息<span>标签:

    <form id="form">
        {% csrf_token %}
        {% for field in form %}
            {# 一个个获取字段对象 #}
            <div class="form-group">
                <label for="{{ field.auto_id }}">{{ field.label }}</label>
                {{ field }} <span class="error pull-right"></span>
            </div>
        {% endfor %}
        ......
    <form>
    

    (2)register.html中的注册点击事件ajax请求回调函数做如下处理

    // 基于Ajax提交数据
    $(".reg-btn").click(function () {
        var formdata =  new FormData();
        var request_data = $("#form").serializeArray();
        $.each(request_data, function (index, data) {
            // 依次循环,每次的操作
            formdata.append(data.name, data.value)
        });
        formdata.append("avatar", $("#avatar")[0].files[0]);  // 拿到用户传递的文件对象
    
        $.ajax({
            url: "",
            type: "post",
            contentType: false,
            processData: false,
            data: formdata,
            success:function (data) {
                // console.log(data)
                if (data.user){
                    // 注册成功
                } else {
                    // 注册失败
                    console.log(data.msg);
                    $.each(data.msg, function (field, error_list) { // 键:每个字段字符串; 值:错误信息列表
                        console.log(field, error_list);
                        $("#id_"+field).next().html(error_list[0]);
                    })
                }
            }
        })
    })

    注意:

     1)在回调函数中处理注册信息,success:function(data){}中的data就是视图函数register中返回的response对象:

    from django.http import JsonResponse
    def register(request):
        if request.is_ajax():
            ...
        return JsonResponse(response)

      字典放进去直接序列化,ajax拿到的就是对象,两边都不需要进行json的序列化与反序列化

    $.ajax({
        url: "",
        type: "post",
        contentType: false,
        processData: false,
        data: formdata,
        success:function (data) {
            // console.log(data)
            if (data.user){
                // 注册成功
            } else {
                // 注册失败
                console.log(data.msg);
            }
        }
    })    
    

      在视图函数中,当校验成功时,会给字典response的键user赋值,否则仍为None。Ajax中依此判断注册是否成功。

     2)当注册失败时,处理视图函数中返回的response的键msg的值(错误信息):

    $.ajax({
        url: "",
        type: "post",
        contentType: false,
        processData: false,
        data: formdata,
        success:function (data) {
            // console.log(data)
            if (data.user){
                // 注册成功
            } else {
                // 注册失败
                console.log(data.msg);
                $.each(data.msg, function (field, error_list) { // 键:每个字段字符串; 值:错误信息列表
                    console.log(field, error_list)
                })
            }
        }
    })
    

      浏览器控制台输出如下:

      

     3)由于错误字段的名字和input标签的id相同:可以用"id"和field拼接为id_user、id_re_pwd等input标签的id值

       又由于error_list是一个数组,且数组中保存的是一个字符串,因此只需要取数组的第一条记录即可:

    $("#id_"+field).next().html(error_list[0]);
    

    (3)给错误信息设置样式

    <style>
        #avatar_img {
            margin-left: 20px;
        }
        #avatar {
            display: none;
        }
        .error {
            color: red;
        }
    </style>

      显示效果如下:

      

    (4)每次点击提交,在展示span标签的错误信息前,都应该将所有span标签清空一次

    $.ajax({
            url: "",
            type: "post",
            contentType: false,
            processData: false,
            data: formdata,
            success:function (data) {
                // console.log(data)
                if (data.user){
                    // 注册成功
                } else {
                    // 注册失败
                    console.log(data.msg);
    // 清空错误信息 $("span.error").html("");
    $.each(data.msg, function (field, error_list) { // 键:每个字段字符串; 值:错误信息列表 console.log(field, error_list); $("#id_"+field).next().html(error_list[0]); }) } } })

      这样在第一次所有注册信息都错误,第二次注册填写的用户名正确时,就只会显示当前错误的提示了。

      

    (5)进一步优化,让错误的注册信息的input标签边框变红

    // 基于Ajax提交数据
    $(".reg-btn").click(function () {
        var formdata =  new FormData();
        var request_data = $("#form").serializeArray();
        $.each(request_data, function (index, data) {
            // 依次循环,每次的操作
            formdata.append(data.name, data.value)
        });
    
        formdata.append("avatar", $("#avatar")[0].files[0]);  // 拿到用户传递的文件对象
    
        $.ajax({
            url: "",
            type: "post",
            contentType: false,
            processData: false,
            data: formdata,
            success:function (data) {
                // console.log(data)
                if (data.user){
                    // 注册成功
                } else {
                    // 注册失败
                    console.log(data.msg);
                    // 清空错误信息
                    $("span.error").html("");
                    $(".form-group").removeClass("has-error");
                    // 展示此次提交的错误信息
                    $.each(data.msg, function (field, error_list) { // 键:每个字段字符串; 值:错误信息列表
                        console.log(field, error_list);
                        $("#id_"+field).next().html(error_list[0]);
                        $("#id_"+field).parent().addClass("has-error");
                    })
                }
            }
        })
    })

    显示效果:

      

    注意:

     1)对input标签的父标签添加bootstrap的类has-error。

      

      Bootstrap 对表单控件的校验状态,如 error、warning 和 success 状态,都定义了样式。使用时,添加 .has-warning.has-error 或 .has-success 类到这些控件的父元素即可。

     2)清空错误信息时也需要同时清掉class = "form-group"的组件的表框变红样式

    // 清空错误信息
    $("span.error").html("");
    $(".form-group").removeClass("has-error");
    // 展示此次提交的错误信息
    $.each(data.msg, function (field, error_list) { // 键:每个字段字符串; 值:错误信息列表
        console.log(field, error_list);
        $("#id_"+field).next().html(error_list[0]);
        $("#id_"+field).parent().addClass("has-error");
    })
    

    5、forms组件的局部钩子和全局钩子校验

    (1)首先代码解耦

      创建/cnblog/blog/Myforms.py文件存放forms组件和钩子,并在views.py中引入forms

    from blog.Myforms import UserForm

    (2)应用引入widgets模块,配置修改forms类参数

      在字段构造参数中设置"error_messages"修改字段错误提示消息

    from django import forms
    from django.forms import widgets
    from blog.models import UserInfo
    
    class UserForm(forms.Form):
        user = forms.CharField(max_length=32,
                               error_messages={"required": "该字段不能为空"},
                               label="用户名",
                               widget=widgets.TextInput(attrs={"class": "form-control"},)
                               )
        pwd = forms.CharField(max_length=32,
                              label="密码",
                              widget=widgets.PasswordInput(attrs={"class": "form-control"}, )
                              )
        re_pwd = forms.CharField(max_length=32,
                                 label="确认密码",
                                 widget=widgets.PasswordInput(attrs={"class": "form-control"},)
                                 )
        email = forms.EmailField(max_length=32,
                                 label="邮箱",
                                 widget=widgets.EmailInput(attrs={"class": "form-control"},)
                                 )

      其中"requried"负责字段不能为空消息提示;"invalid"负责格式错误消息提示等.

      

    (3)添加局部钩子,校验用户是否注册

    from django import forms
    from django.forms import widgets
    from blog.models import UserInfo
    from django.core.exceptions import ValidationError
    
    class UserForm(forms.Form):
        ...
    
        # 局部钩子
        def clean_user(self):
            val = self.cleaned_data.get("user")
            user = UserInfo.objects.filter(username=val).first()
            if not user:
                return val
            else:
                raise ValidationError("该用户已注册!")

    显示效果:

      

    注意:

     1)引入ValidationError模块

    from django.core.exceptions import ValidationError
    

     2)val = self.cleaned_data.get("user")  在用户输入的用户名不为空时,符合字段要求,因此添加在cleaned_data中,通过此方法可以拿到用户输入的用户名。

     3)再根据这个用户名去数据库表blog_userinfo中查找相关用户:

    user = UserInfo.objects.filter(username=val).first()
    

      如果这个用户名不存在则通过校验,如果存在则通过ValidationError设置错误提示:

    if not user:
        return val
    else:
        raise ValidationError("该用户已注册!")
    

     4)需要注意的是当检验通过时,需要返回的是用户输入的用户名而不是数据库中查找的记录,如果返回的值有问题,在生成用户记录时,往往会报ValueError: The given username must be set。这样的错误。

    (4)添加全局钩子——检验两次密码不一致

    from django import forms
    from django.forms import widgets
    from blog.models import UserInfo
    from django.core.exceptions import ValidationError
    
    class UserForm(forms.Form):
        ......
    
        # 局部钩子
        def clean_user(self):
            user = self.cleaned_data.get("user")
            user = UserInfo.objects.filter(username=user).first()
            if not user:
                return user
            else:
                raise ValidationError("该用户已注册!")
    
        # 全局钩子
        def clean(self):
            pwd = self.cleaned_data.get("pwd")
            re_pwd = self.cleaned_data.get("re_pwd")
    
            if pwd and re_pwd:
                # 如果两个都有值
                if pwd == re_pwd:
                    # 验证成功
                    return self.cleaned_data
                else:
                    # 验证失败
                    raise ValidationError("两次密码不一致!")
            else:
                # 如果任有一个没有值则不做处理
                return self.cleaned_data

    注意:

     1)在register.html中ajax回调函数中,通过__all__取到全局错误信息:

    $.ajax({
        url: "",
        type: "post",
        contentType: false,
        processData: false,
        data: formdata,
        success:function (data) {
            // console.log(data)
            if (data.user){
                // 注册成功
            } else {
                // 注册失败
                console.log(data.msg);
                // 清空错误信息
                $("span.error").html("");
                $(".form-group").removeClass("has-error");
                // 展示此次提交的错误信息
                $.each(data.msg, function (field, error_list) { // 键:每个字段字符串; 值:错误信息列表
                    console.log(field, error_list);
                    if (field == "__all__"){
                        // 全局错误信息
                        $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error");
                    }
                    $("#id_"+field).next().html(error_list[0]);
                    $("#id_"+field).parent().addClass("has-error");
                })
            }
        }
    })
    

      查看页面信息:

      

     2)如上所示在找到field=="__all__"的情况下,在id=“id_re_pwd”的input标签的下一个兄弟标签即span标签中填写错误信息,并在input的标签的父标签div中添加类has_error,修改input标签的显示样式。

    if (field == "__all__"){
        // 全局错误信息
        $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error");
    }
    

     3)当两个密码都有输入时,验证两个密码是否一致;当两个密码任有一个没输入则不做处理

    if pwd and re_pwd:
        # 如果两个都有值
        if pwd == re_pwd:
            # 验证成功
            return self.cleaned_data
        else:
            # 验证失败
            raise ValidationError("两次密码不一致!")
    else:
        # 如果任有一个没有值则不做处理
        return self.cleaned_data
    

    显示效果:

      

    四、数据校验通过——FileField字段处理

    1、在register.html的ajax回调函数中注册成功时跳转到登录页面登录

    $.ajax({
        url: "",
        type: "post",
        contentType: false,
        processData: false,
        data: formdata,
        success:function (data) {
            // console.log(data)
            if (data.user){
                // 注册成功,跳转登录页面
                location.href = "/login/"
            } else {
                // 注册失败
                console.log(data.msg);
                // 清空错误信息
                $("span.error").html("");
                $(".form-group").removeClass("has-error");
                // 展示此次提交的错误信息
                $.each(data.msg, function (field, error_list) { // 键:每个字段字符串; 值:错误信息列表
                    console.log(field, error_list);
                    if (field == "__all__"){
                        // 全局错误信息
                        $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error");
                    }
                    $("#id_"+field).next().html(error_list[0]);
                    $("#id_"+field).parent().addClass("has-error");
                })
            }
        }
    })
    

    2、在视图函数register中处理校验通过后,用户注册操作

    from blog.Myforms import UserForm
    from blog.models import UserInfo
    
    
    def register(request):
        if request.is_ajax():  # 也可以使用  if request.method == "POST":
            # 如果是一个Ajax请求
            print(request.POST)   # 所有提交的数据
            form = UserForm(request.POST)  # 创建UserForm对象,传入当前提交的字典
            response = {"user": None, "msg": None}
    
            if form.is_valid():  # form.is_valid是帮忙校验返回布尔值的,true或false(所有都通过才返回true)
                # 类定义的字段全符合要求,返回true
                response["user"] = form.cleaned_data.get("user")
    
                # 生成一条用户记录
                user = form.cleaned_data.get("user")
                pwd = form.cleaned_data.get("pwd")
                email = form.cleaned_data.get("email")
                avatar_obj = request.FILES.get("avatar")
                user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj)  # 用户认证组件使用create_user辅助函数创建用户
    
            else:
                # 包含错误信息返回false
                print(form.cleaned_data)  # 字段值符合要求的放在cleaned_data
                print(form.errors)   # 字段不符合要求的对应的键作为键,错误信息作为值
                response["msg"] = form.errors
    
            return JsonResponse(response)
    
        form = UserForm()   # 实例化form对象
        return render(request, "register.html", {"form": form})  # 注入要传入的是一个字典
    

      注意:

    (1)需要在视图中引入模型

    from blog.models import UserInfo
    

    (2)form.cleaned_data:字段值符合要求的放在cleaned_data中。字典数据类型。由于校验通过,所以用户名、密码、邮箱信息都可以用如下方法拿到:

    # 生成一条用户记录
    user = form.cleaned_data.get("user")
    pwd = form.cleaned_data.get("pwd")
    email = form.cleaned_data.get("email")
    

    (3)django操作models.FileField数据类型:

    class UserInfo(AbstractUser):
        ......
        avatar = models.FileField(upload_to='avatars/', default="/avatars/default.png")   # 该字段存放每个用户的头像文件
    
        def __str__(self):
            return self.username

      FileField与ImageField用法类似,FileField这个字段一定要接受一个文件对象;ImageField一定要接受一个图片对象
      接受文件对象,django会做的事情:默认下载文件到项目的根目录;设置upload_to字段后,会下载到项目根目录的avatars目录下(没有该目录则创建一个)。
      user_obj的avatar存的是文件的相对路径

    (4)用户认证组件使用create_user辅助函数创建用户

    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj)
    

    3、注册验证

    (1)注册完成后页面跳转到登录页面

      

    (2)数据库的blog_userinfo表产生一条新的用户数据,且avatar保存的确实是文件的相对路径

      

    (3)设置upload_to字段后,会下载到项目根目录的avatars目录下,没有提前创建目录的情况下会自动创建:

      

    五、media配置——MEDIA_ROOT

    1、Django静态文件区分

      Django有两种静态文件:

      /static/:服务器自己用到的文件,如:js,css,img

      /media/:用户上传的文件

     2、在settings.py中配置MEDIA_ROOT

    MEDIA_ROOT = os.path.join(BASE_DIR, "media")
    

      一旦配置了MEDIA_ROOT,Django会将文件对象下载到MEDIA_ROOT中的avatar目录中。如果没有avatar文件夹,Django也会自动创建,user_obj的avatar存的是文件对象。

    3、注册一个新用户项目效果

      

    4、media配置的用处

      在使用FileFIeld或者ImageField时,上传实例化的对象,所有上传的文件都会放在media/"定义的文件夹名称"/,与服务器的文件分开,耦合性非常好!

    5、根据头像上传不同情况生成用户对象

    (1)在注册时,考虑到有的人没有上传头像,可以做如下处理

      首先将default.png拷到/media/avatars/目录下,

      然后修改register视图函数在生成用户记录时做如下判定:

    # 生成一条用户记录
    user = form.cleaned_data.get("user")
    pwd = form.cleaned_data.get("pwd")
    email = form.cleaned_data.get("email")
    avatar_obj = request.FILES.get("avatar")
    if avatar_obj:
        # 如果上传了头像,即有值
        # 用户认证组件使用create_user辅助函数创建用户
        user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj)
    else:
        # 如果没有传头像,就用默认的default.png
        user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)

    (2)生成用户对象代码优化

    from blog.Myforms import UserForm
    from blog.models import UserInfo
    
    def register(request):
        if request.is_ajax():  # 也可以使用  if request.method == "POST":
            # 如果是一个Ajax请求
            print(request.POST)   # 所有提交的数据
            form = UserForm(request.POST)  # 创建UserForm对象,传入当前提交的字典
            response = {"user": None, "msg": None}
    
            if form.is_valid():  # form.is_valid是帮忙校验返回布尔值的,true或false(所有都通过才返回true)
                # 类定义的字段全符合要求,返回true
                response["user"] = form.cleaned_data.get("user")
    
                # 生成一条用户记录
                user = form.cleaned_data.get("user")
                pwd = form.cleaned_data.get("pwd")
                email = form.cleaned_data.get("email")
                avatar_obj = request.FILES.get("avatar")
    
                extra = {}  # 空字典
                if avatar_obj:
                    extra["avatar"] = avatar_obj  # 传值进空字典
                UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra)  # **extra代表额外的参数
    
                """
                if avatar_obj:
                    # 如果上传了头像,即有值
                    # 用户认证组件使用create_user辅助函数创建用户
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj)
                else:
                    # 如果没有传头像,就用默认的default.png
                    user_obj = UserInfo.objects.create_user(username=user, password=pwd, email=email)
                """
            else:
                # 包含错误信息返回false
                print(form.cleaned_data)  # 字段值符合要求的放在cleaned_data
                print(form.errors)   # 字段不符合要求的对应的键作为键,错误信息作为值
                response["msg"] = form.errors
    
            return JsonResponse(response)
    
        form = UserForm()   # 实例化form对象
        return render(request, "register.html", {"form": form})  # 注入要传入的是一个字典
    

      注意:

     1)create_user方法的源代码如下:

    def create_user(self, username, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(username, email, password, **extra_fields)
    

      说明额外的参数通过键值对的方式传入即可。因此创建空字典extra。

     2)由于user_obj并没有用到,因此删除这个赋值。

    六、MEDIA配置——MEDIA_URL

      客户端浏览器可以访问到static下的数据,但如何能直接访问到media中的数据。

      

    1、在settings.py中配置一个MEDIA_URL

    MEDIA_URL = "/media/"
    

    2、在路由控制中添加media配置

    from django.contrib import admin
    from django.urls import path,re_path
    from django.views.static import serve
    from blog import views
    from cnblog import settings
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.login),
        path('index/', views.index),
        path('get_validCode_img/', views.get_validCode_img),
        path('register/', views.register),
    
        # media配置
        # 匹配以media开头的任意路径
        re_path(r"media/(?P<path>.*)$", serve, {"document_root": settings.MEDIA_ROOT})
    ]
    

    3、显示效果

      

    七、代码格式规范

    1、导包规范

      导入的包排列顺序是:python标准库、第三方插件的包、自己定义的包。

    2、在代码中去除print

      变量解释可以放在函数下注释来描述:

      

    3、遵循PEP8 Python 编码规范

      option+command+L,Python编辑器自动规范代码。

  • 相关阅读:
    .net测试篇之Moq行为配置
    .net测试篇之Moq框架简单使用
    .net测试篇之测试神器Autofixture在几个复杂场景下的使用示例以及与Moq结合
    .net测试篇之测试神器Autofixture Generator使用与自定义builder
    .net测试篇之测试神器Autofixture基本配置一
    .net测试篇之单元测试/集成测试神器Autofixture
    .netcore持续集成测试篇之web项目验收测试
    .netcore持续集成测试篇之 .net core 2.1项目集成测试
    .netcore持续集成测试篇之MVC层单元测试
    .netcore持续集成测试篇之测试方法改造
  • 原文地址:https://www.cnblogs.com/xiugeng/p/9361466.html
Copyright © 2011-2022 走看看