10. 注冊(cè)視圖

前面我們已經(jīng)完成了項(xiàng)目大部分內(nèi)容,現(xiàn)在還剩下重要的注冊(cè)功能沒(méi)有實(shí)現(xiàn)。

一、創(chuàng)建forms

顯而易見(jiàn),我們的注冊(cè)頁(yè)面也需要一個(gè)form表單。同樣地,在/login/forms.py中添加一個(gè)新的表單類:

class RegisterForm(forms.Form):
    gender = (
        ('male', "男"),
        ('female', "女"),
    )
    username = forms.CharField(label="用戶名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password1 = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    password2 = forms.CharField(label="確認(rèn)密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label="郵箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
    sex = forms.ChoiceField(label='性別', choices=gender)
    captcha = CaptchaField(label='驗(yàn)證碼')

說(shuō)明:

  • gender字典和User模型中的一樣,其實(shí)可以拉出來(lái)作為常量共用,為了直觀,特意重寫(xiě)一遍;
  • password1和password2,用于輸入兩遍密碼,并進(jìn)行比較,防止誤輸密碼;
  • email是一個(gè)郵箱輸入框;
  • sex是一個(gè)select下拉框;
  • 沒(méi)有添加更多的input屬性

二、完善register.html

同樣地,類似login.html文件,我們手工在register.html中編寫(xiě)forms相關(guān)條目:

{% load static %}
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- 上述meta標(biāo)簽*必須*放在最前面,任何其他內(nèi)容都*必須*跟隨其后! -->
    <!-- Bootstrap CSS -->
    <link  rel="stylesheet">
    <link href="{% static 'login/css/register.css' %}" rel="stylesheet"/>
    <title>注冊(cè)</title>
  </head>
  <body>
    <div class="container">
            <div class="col">
                <form class="form-register" action="/register/" method="post">

                    {% if register_form.captcha.errors %}
                        <div class="alert alert-warning">{{ register_form.captcha.errors }}</div>
                    {% elif message %}
                        <div class="alert alert-warning">{{ message }}</div>
                    {% endif %}

                  {% csrf_token %}
                  <h3 class="text-center">歡迎注冊(cè)</h3>

                  <div class="form-group">
                      {{ register_form.username.label_tag }}
                      {{ register_form.username}}
                  </div>
                  <div class="form-group">
                      {{ register_form.password1.label_tag }}
                      {{ register_form.password1 }}
                  </div>
                  <div class="form-group">
                      {{ register_form.password2.label_tag }}
                      {{ register_form.password2 }}
                  </div>
                  <div class="form-group">
                      {{ register_form.email.label_tag }}
                      {{ register_form.email }}
                  </div>
                  <div class="form-group">
                      {{ register_form.sex.label_tag }}
                      {{ register_form.sex }}
                  </div>
                  <div class="form-group">
                      {{ register_form.captcha.label_tag }}
                      {{ register_form.captcha }}
                  </div>

                  <div>
                      <a href="/login/"  ><ins>直接登錄</ins></a>
                      <button type="submit" class="btn btn-primary float-right">注冊(cè)</button>
                  </div>
                </form>
            </div>
    </div> <!-- /container -->

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    {#    以下三者的引用順序是固定的#}
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script src="https://cdn.bootcss.com/popper.js/1.15.0/umd/popper.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>

  </body>
</html>

需要注意的是:

  • 編寫(xiě)了一個(gè)register.css樣式文件
  • form標(biāo)簽的action地址為/register/,class為form-register
  • from中傳遞過(guò)來(lái)的表單變量名字為register_form
  • 最下面的鏈接修改為直接登錄的鏈接

register.css樣式文件的代碼很簡(jiǎn)單,如下所示:

body {
  height: 100%;
  background-image: url('../image/bg.jpg');
}
.form-register {
  width: 100%;
  max-width: 400px;
  padding: 15px;
  margin: 0 auto;
}
.form-group {
  margin-bottom: 5px;
}
form a{
  display: inline-block;
  margin-top:25px;
  line-height: 10px;
}

三、實(shí)現(xiàn)注冊(cè)視圖

進(jìn)入/login/views.py文件,現(xiàn)在來(lái)完善我們的register()視圖:

def register(request):
    if request.session.get('is_login', None):
        return redirect('/index/')

    if request.method == 'POST':
        register_form = forms.RegisterForm(request.POST)
        message = "請(qǐng)檢查填寫(xiě)的內(nèi)容!"
        if register_form.is_valid():
            username = register_form.cleaned_data.get('username')
            password1 = register_form.cleaned_data.get('password1')
            password2 = register_form.cleaned_data.get('password2')
            email = register_form.cleaned_data.get('email')
            sex = register_form.cleaned_data.get('sex')

            if password1 != password2:
                message = '兩次輸入的密碼不同!'
                return render(request, 'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:
                    message = '用戶名已經(jīng)存在'
                    return render(request, 'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:
                    message = '該郵箱已經(jīng)被注冊(cè)了!'
                    return render(request, 'login/register.html', locals())

                new_user = models.User()
                new_user.name = username
                new_user.password = password1
                new_user.email = email
                new_user.sex = sex
                new_user.save()

                return redirect('/login/')
        else:
            return render(request, 'login/register.html', locals())
    register_form = forms.RegisterForm()
    return render(request, 'login/register.html', locals())

從大體邏輯上,也是先實(shí)例化一個(gè)RegisterForm的對(duì)象,然后使用is_valide()驗(yàn)證數(shù)據(jù),再?gòu)?code>cleaned_data中獲取數(shù)據(jù)。

重點(diǎn)在于注冊(cè)邏輯,首先兩次輸入的密碼必須相同,其次不能存在相同用戶名和郵箱,最后如果條件都滿足,利用ORM的API,創(chuàng)建一個(gè)用戶實(shí)例,然后保存到數(shù)據(jù)庫(kù)內(nèi)。

對(duì)于注冊(cè)的邏輯,不同的生產(chǎn)環(huán)境有不同的要求,請(qǐng)跟進(jìn)實(shí)際情況自行完善,這里只是一個(gè)基本的注冊(cè)過(guò)程,不能生搬照抄。

讓我們看一下注冊(cè)的頁(yè)面:

image.png

你可以嘗試用不同的情況進(jìn)行注冊(cè),然后觀察錯(cuò)誤信息的提示:

image.png

最后進(jìn)行一次成功地注冊(cè),會(huì)自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面。我們進(jìn)入admin后臺(tái),查看一下用戶列表:

image.png
image.png

四、密碼加密

等等!我們好像忘了什么!我們到現(xiàn)在都還一直在用明文的密碼!

對(duì)于如何加密密碼,有很多不同的途徑,其安全程度也高低不等。這里我們使用Python內(nèi)置的hashlib庫(kù),使用哈希值的方式加密密碼,可能安全等級(jí)不夠高,但足夠簡(jiǎn)單,方便使用,不是么?

首先在login/views.py中編寫(xiě)一個(gè)hash函數(shù):

import hashlib

def hash_code(s, salt='mysite'):# 加點(diǎn)鹽
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())  # update方法只接收bytes類型
    return h.hexdigest()

使用了sha256算法,加了點(diǎn)鹽。具體的內(nèi)容可以參考站點(diǎn)內(nèi)的Python教程中hashlib庫(kù)章節(jié)。

然后,我們還要對(duì)login()和register()視圖進(jìn)行一下修改:

from django.shortcuts import render
from django.shortcuts import redirect
from . import models
from . import forms
import hashlib
# Create your views here.


def hash_code(s, salt='mysite'):
    h = hashlib.sha256()
    s += salt
    h.update(s.encode())
    return h.hexdigest()


def index(request):
    if not request.session.get('is_login', None):
        return redirect('/login/')
    return render(request, 'login/index.html')


def login(request):
    if request.session.get('is_login', None):  # 不允許重復(fù)登錄
        return redirect('/index/')
    if request.method == 'POST':
        login_form = forms.UserForm(request.POST)
        message = '請(qǐng)檢查填寫(xiě)的內(nèi)容!'
        if login_form.is_valid():
            username = login_form.cleaned_data.get('username')
            password = login_form.cleaned_data.get('password')

            try:
                user = models.User.objects.get(name=username)
            except :
                message = '用戶不存在!'
                return render(request, 'login/login.html', locals())

            if user.password == hash_code(password):
                request.session['is_login'] = True
                request.session['user_id'] = user.id
                request.session['user_name'] = user.name
                return redirect('/index/')
            else:
                message = '密碼不正確!'
                return render(request, 'login/login.html', locals())
        else:
            return render(request, 'login/login.html', locals())

    login_form = forms.UserForm()
    return render(request, 'login/login.html', locals())


def register(request):
    if request.session.get('is_login', None):
        return redirect('/index/')

    if request.method == 'POST':
        register_form = forms.RegisterForm(request.POST)
        message = "請(qǐng)檢查填寫(xiě)的內(nèi)容!"
        if register_form.is_valid():
            username = register_form.cleaned_data.get('username')
            password1 = register_form.cleaned_data.get('password1')
            password2 = register_form.cleaned_data.get('password2')
            email = register_form.cleaned_data.get('email')
            sex = register_form.cleaned_data.get('sex')

            if password1 != password2:
                message = '兩次輸入的密碼不同!'
                return render(request, 'login/register.html', locals())
            else:
                same_name_user = models.User.objects.filter(name=username)
                if same_name_user:
                    message = '用戶名已經(jīng)存在'
                    return render(request, 'login/register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:
                    message = '該郵箱已經(jīng)被注冊(cè)了!'
                    return render(request, 'login/register.html', locals())

                new_user = models.User()
                new_user.name = username
                new_user.password = hash_code(password1)
                new_user.email = email
                new_user.sex = sex
                new_user.save()

                return redirect('/login/')
        else:
            return render(request, 'login/register.html', locals())
    register_form = forms.RegisterForm()
    return render(request, 'login/register.html', locals())


def logout(request):
    if not request.session.get('is_login', None):
        return redirect('/login/')

    request.session.flush()
    # del request.session['is_login']
    return redirect("/login/")

注意其中關(guān)于密碼處理的部分!

好了,我們可以來(lái)驗(yàn)證一下了!但是,請(qǐng)先在admin后臺(tái)里,把我們前面創(chuàng)建的測(cè)試用戶全部刪除!因?yàn)樗鼈兊拿艽a沒(méi)有使用哈希算法加密,已經(jīng)無(wú)效了。

重啟服務(wù)器,進(jìn)入注冊(cè)頁(yè)面,新建一個(gè)用戶,然后進(jìn)入admin后臺(tái),查看用戶的密碼情況:

image.png

再使用該用戶登錄一下,大功告成!

可以看到密碼長(zhǎng)度根據(jù)你哈希算法的不同,已經(jīng)變得很長(zhǎng)了,所以前面model中設(shè)置password字段時(shí),不要想當(dāng)然的將max_length設(shè)置為16這么小的數(shù)字。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、創(chuàng)建項(xiàng)目 1.1.創(chuàng)建項(xiàng)目和app django-admin startproject mysite-logi...
    鵬ve閱讀 14,408評(píng)論 4 71
  • 學(xué)習(xí)Django主要覺(jué)得該框架比較容易上手,還有Django-admin后臺(tái)方便使用,但是有些時(shí)候我們用Djang...
    橙子丨Sunty閱讀 10,504評(píng)論 4 24
  • 1、項(xiàng)目介紹 用戶登錄和注冊(cè)系統(tǒng)是所有網(wǎng)頁(yè)設(shè)計(jì)都需要的一個(gè)模塊,導(dǎo)航欄+登錄+注冊(cè),在這個(gè)的基礎(chǔ)上再來(lái)實(shí)現(xiàn)其他各個(gè)...
    魏云舒閱讀 15,158評(píng)論 1 10
  • 4 創(chuàng)建一個(gè)社交網(wǎng)站 在上一章中,你學(xué)習(xí)了如何創(chuàng)建站點(diǎn)地圖和訂閱,并且為博客應(yīng)用構(gòu)建了一個(gè)搜索引擎。在這一章中,你...
    lakerszhy閱讀 2,257評(píng)論 0 7
  • “無(wú)論走到哪里,都應(yīng)該記住,過(guò)去都是假的,回憶是一條沒(méi)有盡頭的路,一切以往的春天都不復(fù)存在?!薄栋倌旯陋?dú)》...
    23罐閱讀 287評(píng)論 0 1

友情鏈接更多精彩內(nèi)容