Django實現登錄注冊(帶驗證碼)


前言

話不多說,先上效果圖:

正文

Django的安裝什么的咱都不說,直接進入正題,我這里使用的Django==3.0.7。新建Django項目也很簡單,在Pycharm里面New Project--->Django,然后設置一下項目名稱基本就可以了,如下圖:

項目建好之后要簡單配置一下,我習慣先把時區和語言改了,然后設置靜態文件的路徑(css, js, img等),都在settings.py里編輯,靜態文件的路徑根據自己的實際路徑來,我習慣放在根目錄下。

LANGUAGE_CODE = 'zh-hans'  # 中文支持

TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = False  # 默認是 True,時間是utc時間,由於我們要用本地時間,所用手動修改為false
STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

HTML文件放在templates目錄下,一般這個目錄都自動建好了,沒有的自己手動建一個,我這里是直接在根目錄下的。

1)新建app——users

manage.py startapp  users

運行上述命令,創建成功的目錄如下(圖中的myforms.py是我后來自己創建的,這個后面會用到):

要實現用戶登錄和注冊功能,需要保存用戶的相關信息。很顯然,我們至少需要一個用戶表User,我這里需要在用戶表里保存下面的信息:

  • 用戶名
  • 密碼
  • 郵箱地址
  • 創建時間

我們先修改models.py文件,在該文件建立用戶的模型,然后遷移到數據庫即可。修改后的models.py如下:

from datetime import datetime

from django.db import models


class User(models.Model):
    """
    用戶信息
    """

    username = models.CharField('賬號', max_length=16, unique=True)
    password = models.CharField('密碼', max_length=16)
    email = models.EmailField('郵箱', unique=True)
    c_time = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['c_time']  # 元數據里定義用戶按創建時間的反序排列,也就是最近的最先顯示
        verbose_name = '用戶'
        verbose_name_plural = '用戶'

    def __str__(self):  # 使用__str__幫助人性化顯示對象信息
        return self.username

在遷移數據庫之前,我們要先配置數據庫,編輯settings.py,找到DATABASES,我這里用的是默認的數據庫,默認的配置如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

如果需要用到MySQL,則配置如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',        #數據庫名字
        'USER': 'root',          #賬號
        'PASSWORD': '123456',      #密碼
        'HOST': '127.0.0.1',    #IP
        'PORT': '3306',                   #端口
    }
}

數據庫配置完成后,接着在settings.py中注冊app:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
]

運行如下命令遷移到數據庫:

python manage.py makemigrations
 
python manage.py migrate

 

在users的admin.py中注冊模型:

from django.contrib import admin
from . import models
# Register your models here.

admin.site.register(models.User)

這個時候登錄后台管理員賬戶,應該可以看到多了一個表,Django是自帶管理后台的,我們只需注冊一個超級管理員即可。

manmage.py createsuperuser

2)圖形驗證碼

為了防止機器人頻繁登陸網站或者破壞分子惡意登陸,很多用戶登錄和注冊系統都提供了圖形驗證碼功能。

驗證碼(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自動區分計算機和人類的圖靈測試)的縮寫,是一種區分用戶是計算機還是人的公共全自動程序。可以防止惡意破解密碼、刷票、論壇灌水,有效防止某個黑客對某一個特定注冊用戶用特定程序暴力破解方式進行不斷的登陸嘗試。

圖形驗證碼在后面配置路由、表單等多處需要,這里我們先介紹。圖形驗證碼只需要安裝一個庫即可——django--simple-captcha,怎么安裝很簡單不再贅述,安裝完成后在settings.py中注冊一下就可以了,如下:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
    'captcha',
]

具體用法在后面講述,這里只是先聲明一下。

3)路由設置

我們這里采用二級路由,具體看代碼:

  • 在項目的urls.py中
urlpatterns = [
    path('user/', include('users.urls')),  # 函數 include() 允許引用其它 URLconfs
    path('admin/', admin.site.urls),
    path('captcha', include('captcha.urls'))
]
  • 在users的urls.py中
urlpatterns = [
    path('admin', admin.site.urls),
    path('login/', views.login),
    path('index/', views.index),
    path('register/', views.register),
    path('logout/', views.logout),
]

4)編寫視圖

這里我就整個直接貼出代碼:

from django.shortcuts import render, redirect
from . import models
from users.myforms import UserForm, RegisterForm
import hashlib


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


def index(request):
    pass
    return render(request, 'index.html')


def login(request):
    if request.session.get('is_login', None):  # 防止重復登錄
        return redirect('/index')

    if request.method == "POST":
        login_form = UserForm(request.POST)
        message = "請檢查填寫的內容!"
        if login_form.is_valid():
            username = login_form.cleaned_data['username']
            password = login_form.cleaned_data['password']
            try:
                user = models.User.objects.get(username=username)
                if user.password == hash_code(password):  # 哈希值和數據庫內的值進行比對
                    request.session['is_login'] = True  # 往session字典內寫入用戶狀態和數據
                    request.session['user_id'] = user.id
                    request.session['user_name'] = user.username
                    return redirect('/index/')
                else:
                    message = "密碼不正確!"
            except:
                message = "用戶不存在!"
        return render(request, 'login.html', locals())
    login_form = UserForm()
    return render(request, 'login.html', locals())


def register(request):
    if request.session.get('is_login', None):  # 登錄狀態不允許注冊
        return redirect("/index/")
    if request.method == "POST":
        register_form = RegisterForm(request.POST)
        message = "請檢查填寫的內容!"
        if register_form.is_valid():  # 獲取數據
            username = register_form.cleaned_data['username']
            password1 = register_form.cleaned_data['password1']
            password2 = register_form.cleaned_data['password2']
            email = register_form.cleaned_data['email']
            if password1 != password2:  # 判斷兩次密碼是否相同
                message = "兩次輸入的密碼不同!"
                return render(request, 'register.html', locals())
            else:
                same_name_user = models.User.objects.filter(username=username)
                if same_name_user:  # 用戶名唯一
                    message = '用戶名已經存在!'
                    return render(request, 'register.html', locals())
                same_email_user = models.User.objects.filter(email=email)
                if same_email_user:  # 郵箱地址唯一
                    message = '該郵箱地址已被注冊!'
                    return render(request, 'register.html', locals())

                # 當一切都OK的情況下,創建新用戶

                new_user = models.User.objects.create()
                new_user.username = username
                new_user.password = hash_code(password1)  # 使用加密密碼
                new_user.email = email
                new_user.save()
                return redirect('/user/login/')  # 自動跳轉到登錄頁面
    register_form = RegisterForm()
    return render(request, 'register.html', locals())


def logout(request):
    if not request.session.get('is_login', None):  # 如果本來就未登錄,也就沒有登出一說
        return redirect("/index/")
    request.session.flush()  # 將session中的所有內容全部清空
    return redirect('/index/')

hash_code函數將密碼由明文轉為密文,我這里采用python自帶的hashlib庫實現。在上面的代碼中重點講一下session和locals(),表單放到后面講。

當session啟用后,傳遞給視圖request參數的HTTPRequest對象將包含一個session屬性,就像一個字典對象一樣,我們可以在Django的任何地方讀寫request.sesssion屬性,或者多次編輯使用它。

Python內置了一個locals()函數,它返回當前所有的本地變量字典,我們可以偷懶將這作為render函數的數據字典參數值,就不用費勁去構造一個形如{'message':message, 'login_form':login_form} 的字典了。這樣做的好處就是大大的方便了我們,但是同時也可能往模板傳入了一些多余的變量數據,造成了數據冗余降低效率。

return render(request, 'login.html', {'message':message, 'login_form':login_form}
 
大約等同於
 
return render(request, 'login.html', locals())

5)Django表單

myforms.py

from captcha.fields import CaptchaField, CaptchaTextInput
from django import forms


class UserForm(forms.Form):
    username = forms.CharField(label="賬號", max_length=16, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password = forms.CharField(label="密碼", max_length=16, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    captcha = CaptchaField(label='驗證碼', widget=CaptchaTextInput(attrs={'class': 'form-control'}))


class RegisterForm(forms.Form):
    username = forms.CharField(label="賬號", max_length=16, widget=forms.TextInput(attrs={'class': 'form-control'}))
    password1 = forms.CharField(label="密碼", max_length=16, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    password2 = forms.CharField(label="確認密碼", max_length=16, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
    email = forms.EmailField(label="郵箱地址", widget=forms.EmailInput(attrs={'class': 'form-control'}))
    captcha = CaptchaField(label='驗證碼', widget=CaptchaTextInput(attrs={'class': 'form-control'}))

說明:

  1. 要先導入forms模板
  2. 所有的表單類都要繼承forms.Form類
  3. 每個表單字段都有自己的字段類型比如CharField,它們分別對應一種HTML語言中<form>內的一個input元素。這一點和Django模板系統的設計非常相似。
  4. label參數用於設置<label>標簽
  5. max_length 限制字段輸入的最大長度。它同時起到兩個作用,一是在瀏覽器頁面限制用戶輸入不可超過字符數,二是在后端服務器驗證用戶輸入的長度也不可超過。
  6. widget = forms.PasswordInput 用於指定該字段在form表單里表現為 <input type='password'> ,也就是密碼輸入框。

Django的表單很重要的一個功能就是自動生成HTML的form表單內容。直接{{ login_form }} 雖然好,啥都不用操心,但是界面真的不盡人意,往往不是我們想要的,在form類里添加Attr屬性即可。

6)HTML文件

login.html

    <div class="col-md-4 col-sm-6 border p-0 offset-md-4 offset-sm-6 my-5">
        <h3 class="text-center bg-info p-2 text-white mb-0">登錄</h3>
        <form class='p-5 bg-white' action="/user/login/" method="post">
            {% if message %}
                <div class="alert alert-warning">{{ message }}</div>
            {% endif %}
            {% csrf_token %}
            <div class="form-group">
                {{ login_form.username.label_tag }}
                {{ login_form.username }}
            </div>
            <div class="form-group">
                {{ login_form.password.label_tag }}
                {{ login_form.password }}
            </div>
            <div class="form-group">
                {{ login_form.captcha.errors }}
                {{ login_form.captcha.label_tag }}
                {{ login_form.captcha }}
            </div>
            <div class="pt-3 pb-5">
                <button type="submit" class="btn btn-primary pull-left mb-5">立即登錄</button>
                <a class="btn btn-danger pull-right mb-5" href="/user/register/">前往注冊</a>
            </div>
        </form>
    </div>

這里額外的添加了一條 {{ login_form.captcha.errors }} 用於明確指示用戶,你的驗證碼不正確。其中驗證圖形碼是否正確的工作都是在后台自動完成的。只需要使用is_valid()這個myforms內置的驗證方法就一起進行了,完全不需要再視圖函數中添加任何的驗證代碼,非常方便快捷!

register.html

    <div class="col-md-4 col-sm-6 border p-0 offset-md-4 offset-sm-6 my-5">
        <h3 class="text-center bg-info p-2 text-white mb-0">注冊</h3>
        <form class='px-5 py-3 bg-white' action="/user/register/" method="post">
            {% if message %}
                <div class="alert alert-warning">{{ message }}</div>
            {% endif %}
            {% csrf_token %}
            <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.captcha.errors }}
                {{ register_form.captcha.label_tag }}
                {{ register_form.captcha }}
            </div>
            <div class="pt-3">
                <p class="text-center m-0"><button type="submit" class="btn btn-primary btn-block mb-5">立即注冊</button></p>
            </div>
        </form>
    </div>

前端只展示了一部分關鍵代碼,采用了bootstrap框架,下面這段js代碼是用來實現圖形驗證碼的切換的(django-simple-captcha沒有提供這個功能):

    <script>
        $(function () {
            // Add refresh button after field (this can be done in the template as well)
            $('img.captcha').after(
                $('<a class="btn btn-sm text-white btn-info pull-right captcha-refresh">看不清</a>')
            );

            $('.captcha-refresh').click(function () {
                var $form = $(this).parents('form');
                var url = location.protocol + "//" + window.location.hostname + ":"
                    + location.port + "/captcha/refresh/";

                // Make the AJAX-call
                $.getJSON(url, {}, function (json) {
                    $form.find('input[name="captcha_0"]').val(json.key);
                    $form.find('img.captcha').attr('src', json.image_url);
                });
                return false;
            });
        });
    </script>

參考博客:https://www.cnblogs.com/wj-1314/p/10751411.html

項目GitHub地址:https://github.com/Marvin-wen/CovidCTImagesDetection


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM