基於forms組件和Ajax實現註冊功能

1、基於forms組件的註冊頁面設計

一、運用forms組件的校驗字段功能實現用戶註冊 

views.py:    (在鉤子中代碼解耦,將form放在cnblog/blog/Myforms.py中)javascript

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})  # 注意要傳入的是一個字典

  注意:css

  (1)在視圖層引入widgets模塊,配置修改forms類參數。在這裏添加了class="form-control"的bootstrap樣式。添加了label標籤。html

  (2)這裏register視圖,實例化的form對象是未綁定數據的form表單。將這個form對象傳入render中,注意傳入的格式必須是字典的形式。java

二、forms表單渲染標籤生成註冊頁面

register.html:python

<!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>

  顯示效果以下所示:jquery

  

2、博客註冊頁面頭像

 一、默認頭像

register.html:ajax

<!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>

  顯示效果:數據庫

  

 

注意:django

(1)添加頭像上傳和默認頭像時,須要優化顯示效果,點擊默認頭像,顯示文件上傳框圖。這裏應用的方法是巧妙使用label標籤,將img標籤嵌套在label標籤中,設置<label for="">中的值與input標籤的id相同。實現點擊label標籤就會默認點擊到了input標籤。設置完後,隱藏input標籤的顯示。json

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

  

二、頭像預覽功能

  首先須要瞭解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)    

3、錯誤信息展現——基於Ajax提交formdata數據

一、在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());

二、在視圖中處理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);

展現效果以下所示:

  

三、基於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則表示數組當前元素。

四、基於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");
})

五、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

顯示效果:

  

4、數據校驗經過——FileField字段處理

一、在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");
            })
        }
    }
})

二、在視圖函數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)

三、註冊驗證

(1)註冊完成後頁面跳轉到登陸頁面

  

 

(2)數據庫的blog_userinfo表產生一條新的用戶數據,且avatar保存的確實是文件的相對路徑

  

(3)設置upload_to字段後,會下載到項目根目錄的avatars目錄下,沒有提早建立目錄的狀況下會自動建立:

  

5、media配置——MEDIA_ROOT

一、Django靜態文件區分

  Django有兩種靜態文件:

  /static/:服務器本身用到的文件,如:js,css,img

  /media/:用戶上傳的文件

 二、在settings.py中配置MEDIA_ROOT

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

  一旦配置了MEDIA_ROOT,Django會將文件對象下載到MEDIA_ROOT中的avatar目錄中。若是沒有avatar文件夾,Django也會自動建立,user_obj的avatar存的是文件對象。

三、註冊一個新用戶項目效果

  

四、media配置的用處

  在使用FileFIeld或者ImageField時,上傳實例化的對象,全部上傳的文件都會放在media/"定義的文件夾名稱"/,與服務器的文件分開,耦合性很是好!

五、根據頭像上傳不一樣狀況生成用戶對象

(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並無用到,所以刪除這個賦值。

6、MEDIA配置——MEDIA_URL

  客戶端瀏覽器能夠訪問到static下的數據,但如何能直接訪問到media中的數據。

  

一、在settings.py中配置一個MEDIA_URL

MEDIA_URL = "/media/"

二、在路由控制中添加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})
]

三、顯示效果

  

7、代碼格式規範

一、導包規範

  導入的包排列順序是:python標準庫、第三方插件的包、本身定義的包。

二、在代碼中去除print

  變量解釋能夠放在函數下注釋來描述:

  

三、遵循PEP8 Python 編碼規範

  option+command+L,Python編輯器自動規範代碼。

相關文章
相關標籤/搜索