Django實現迷你淘寶(三) --- 基於django的用戶驗證系統實現

歡迎查看本系列的其他文章:

  1. postgres安裝與入門
  2. django安裝與入門
  3. 基於django的用戶驗證系統實現
  4. 基於Bootstrap的商品頁面設計與美化
  5. 基於jQuery和aJax的購物車設計與實現

Django實現迷你淘寶(三) — 基於django的用戶驗證系統實現

用戶驗證基本是一個網站的必備功能,至少包含幾個功能:

  • 用戶註冊與登錄
  • 用戶驗證(有些網頁只有登錄用戶才能訪問)
  • 用戶基本信息修改

Django本身就自帶了用戶驗證系統,但針對不同類型的網站我們可能需要不同的信息,如迷你淘寶,需要知道該用戶是否爲賣家等,所以自帶的驗證系統無法完全滿足我們的需求,但是我們可以以此爲基礎,進行開發,更安全也更方便。

本文代碼

1. 數據對象

因爲django內置了一個ORM層,我們不需要直接設計數據庫的table,我們只需要創建一個用戶對象進行操作即可。在第二篇文章裏面,我們已經創建了一個Profile類,具體代碼如下:

from django.contrib.auth.models import User
from django.db import models

# Create your models here.
class Profile(models.Model):
    user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
    is_seller = models.BooleanField(default=False)

    def __str__(self):
        return f'{self.user.username} Profile'

這裏我們通過from django.contrib.auth.models import User導入了django自帶的User對象,該對象已經包含了用戶名,密碼,郵箱等常用字段,並且包含了密碼相關的所有內容(如,加密儲存,安全性驗證等),在此基礎上,針對我們自己的應用增加了一個is_seller字段。

這就是我們需要的所有信息了,下面我們來看看如何把這個類和前端頁面結合起來。

2. 用戶註冊

2.1 django表單

django對於每一種類型的變量(Boolean,Integer等等)都內置了有一個對應的html樣式,想要使用內置的樣式,就需要創建一個表單繼承django的內置表單。在users文件夾內創建一個forms.py文件,寫入如下內容:

from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User


class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']

這裏我們創建了一個UserRegisterForm繼承自django自帶的UserCreationFormclass Meta裏面,model用於指定該表單對應的“模型”是什麼(每個表單都應該有一個對應的“模型”),fields用於指定我們需要該模型內的哪些字段。

2.2 視圖函數

users/views.py裏面增加註冊函數

def register(request):
    if request.method == 'POST':
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect(reverse("home"))
    else:
        form = UserRegisterForm()
    return render(request, 'users/register.html', {'form': form})

這裏首先判斷request是GET還是POST,如果是GET我們就創建一個空白的表單傳給html模板,假如是POST我們就從request中讀取表單信息,並調用內置的is_valid()函數對錶單數據進行驗證,驗證成功的話就保存數據。由於我們在UserRegisterForm裏面聲明瞭該表單關聯的對象是User,所以form.save()會創建並保存一個User對象。註冊成功之後,我們將用戶重定向回主頁面。

同時別忘了去users/url.py裏面註冊該視圖函數:

from django.urls import path

from . import views

urlpatterns = [
    path('', views.home, name="home"),
    path('register/', views.register, name='register'),
]

2.3 HTML模板文件

2.3.1 基礎模板文件

首先在users文件夾下面創建一個templates文件夾並在裏面創建users文件夾,這個文件夾將會用來存放我們所有的HTML模板文件。這裏爲了以後能更方便的在每個頁面使用Bootstrap(下一篇文章會詳細講解),我們首先新建一個base.html,寫入如下內容:

<!DOCTYPE html>
<html lang="en">
<head>
    {% load static %}
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <title>迷你淘寶</title>
</head>
<body>

<main role="main">
    {% block content %}
    {% endblock %}
</main>

{% block script %}
{% endblock %}
</body>
</html>

這個文件會作爲其他所有html文件的“容器“,後期我們可以通過這個“容器”,快速將Bootstrap應用到所有界面。

2.3.2 註冊頁面模板

創建一個register.html用於用戶註冊

{% extends "users/base.html" %}
{% block content %}
    <form method="POST">
        {% csrf_token %}
        <fieldset>
            <legend>Join Today</legend>
            {{ form.as_p }}
        </fieldset>
        <button class="btn btn-outline-info" type="submit">Sign Up</button>
    </form>
{% endblock content %}

模板文件裏面通過{{ form }}即可取得傳入的form對象,想要將其渲染出來,只需要用{{ form.as_p }}即可完成(還有{{ form.as_table }}等其他樣式,詳見這裏),十分的方便。

此時文件目錄如下

.
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── migrations
│   ├── 0001_initial.py
│   ├── __init__.py
├── models.py
├── templates
│   └── users
│       ├── base.html
│       └── register.html
├── tests.py
├── urls.py
└── views.py

運行程序,輸入網址http://127.0.0.1:8000/register/,你應該能看到如下頁面
在這裏插入圖片描述
我們不需要寫一行具體的代碼,就可以生成整個表單。不過現在這個表單還很醜,別急,下一篇文章會教你如何使用Bootstrap美化表單。

2.4 利用signals創建Profile

細心的讀者可能已經發現了一點問題,就是我們通過表單創建的是一個User對象,但是我們實際需要的是一個Profile對象(包含了我們自己的field),爲了解決這個問題,我們又引入一個新的概念,信號(signals),django提供各種各樣的信號,你可以通過捕捉這些信號,幹一些有趣的事情。
在users文件夾下面新建signals.py,並添加如下代碼:

from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile


@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)


@receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
    instance.profile.save()

這裏我們捕獲了post_save信號(使用annotation),這個信號會在save()的最後被調用,所以每一次創建一個新的User對象,就會調用這個函數並創建一個對應的Profile對象。
爲了方便管理所有信號,我們單獨新建了一個文件,所以我們需要去“註冊”一下這些信號,修改users/apps.py文件,增加一個ready函數(會在該app運行起來後被調用),並在函數內導入signals文件:

class UsersConfig(AppConfig):
    name = 'users'

    def ready(self):
        # import all signals
        import users.signals

常見問題:

  1. 如果現在還是無法捕獲信號,嘗試在users/__init__.py裏面添加一行default_app_config = 'users.apps.UsersConfig'

2.5 結果驗證

爲了驗證我們是否註冊成功,我們在主頁面加一些內容,把所有已註冊用戶的名字顯示出來。

2.5.1 修改views.pyhome函數

def home(request):
    profiles = Profile.objects.all()
    context = {"profiles": profiles}
    return render(request, 'users/home.html', context)

這裏我們首先通過Profile.objects.all()獲得所有的已註冊用戶信息,再將其傳入home.html

2.5.2 新建home.html函數

users/templates/users目錄下新建home.html文件,並寫入如下內容

{% extends "users/base.html" %}
{% block content %}
    <h1>Hello world</h1>
    {% if profiles %}
        <ul>
            <h2>User list</h2>
            {% for p in profiles %}
                <li>{{ p.user.username }}</li>
            {% endfor %}
        </ul>
    {% else %}
        <h2>No user yet.</h2>
    {% endif %}
    <a href="{% url "register" %}">Register</a>
{% endblock %}

這裏用{% for p in profiles %}循環取出所有的已註冊用戶

成功運行之後,大家訪問127.0.0.1就可以看到類似的界面了(我這裏已經有兩個用戶)
在這裏插入圖片描述

2.5.3 其他驗證方法

有的小夥伴可能會說,我只是想驗證一下數據是否成功插入,你讓我寫一個頁面,太麻煩了吧。不要急,也有更簡單的方法:

  • 首先python manage.py shell進入django的shell
  • 然後from users.models import Profile導入我們的類
  • 最後Profile.objects.all()

結果如下
在這裏插入圖片描述

3. 用戶登錄

用戶註冊後面緊接着肯定是用戶登錄功能了

3.1 登錄頁面模板

新建login.html,並寫入

{% extends "users/base.html" %}
{% block content %}
    <div class="content-section">
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Log In</legend>
                {{ form.as_p }}
            </fieldset>

            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Login</button>
            </div>
        </form>
        <div class="border-top pt-3">
            <small class="text-muted">
                Need An Account? <a class="ml-2" href="{% url 'register' %}">Sign Up Now</a>
            </small>
        </div>
    </div>
{% endblock content %}

同樣的,這裏還是用到django的表單來簡化代碼,核心代碼就是{{ form.as_p }}
這個頁面沒有太多內容,就是一個表單,用於給用戶輸入登錄信息,然後登錄按鈕。

3.2 登錄頁面視圖函數

由於登錄頁面的處理邏輯十分簡單,而且也相對固定(基本邏輯就是獲得用戶數據,驗證,返回結果),所以django連這個都幫我們寫好了,在django.contrib.auth包下面,我們打開urls.py添加一行

path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),

同時別忘了導入包

from django.contrib.auth import views as auth_views

這時候的urls.py應該長這樣

from django.urls import path
from django.contrib.auth import views as auth_views

from . import views

urlpatterns = [
    path('', views.home, name="home"),
    path('register/', views.register, name='register'),
    path('login/', auth_views.LoginView.as_view(template_name='users/login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(template_name='users/logout.html'), name='logout'),
]

細心的小夥伴可能發現了,我們沒有指定登陸成功後跳轉到哪裏,假如你試一下你會發現,django默認會跳轉到127.0.0.1;8000/account/profile頁面,但是我們可能想讓他跳轉到我們的首頁,此時你需要修改一下setting.py文件,在文件最後面增加一行

LOGIN_REDIRECT_URL = 'home'

3.3 登出功能

登出和登錄十分類似,django也給我們內置好了,直接寫一個html並調用內置函數即可,這裏就不詳細講解了。

4. 用戶驗證

前面說了這麼多,核心的目的還是爲了區分“合法用戶”和“非法用戶”,因爲有些頁面只有已登錄的用戶才能訪問(如:個人信息,購物車,歷史訂單等)。用戶驗證主要有兩方面的需求

  1. 某個頁面只有已登錄用戶才能訪問,未登錄的無法訪問
  2. 某個頁面大家都可以訪問,但是已登錄用戶和未登錄用戶的界面不一樣

4.1 拒絕未登錄用戶

這裏我們增加一個profile頁面,模仿任意只能被已登錄用戶訪問的頁面,細節就不再贅述,大致過程:新建一個html —> 新建一個視圖函數 —> 註冊視圖函數 —> 在主頁面增加一個鏈接,跳轉到新頁面。
此時首頁應該長這樣:
在這裏插入圖片描述
profile頁面的html如下:

{% extends "users/base.html" %}
{% block content %}
    <h2>{{ user.username }}</h2>
{% endblock content %}

這個頁面,我們只是簡單的顯示一下用戶的用戶名。
注意,這裏我們不需要傳入user對象也可以訪問,因爲django默認的在每個request裏面都會有一個user對象,這也是使用django自帶驗證系統的一大好處。

爲了拒絕未登錄用戶,我們只需要在profile的視圖函數裏面加一個annotation就好

from django.contrib.auth.decorators import login_required

@login_required
def profile(request):
    return render(request, 'users/profile.html')

未登錄用戶訪問這個頁面的“正確”操作應該是把他導向登錄頁面,所以我們在setting.py文件裏面增加一行,告訴django我們的登錄頁面URL

LOGIN_URL = 'login'

此時,運行程序,直接訪問該頁面會被重定向到登錄界面,登錄後再次訪問,就會看到你的用戶名。

4.2 根據用戶身份動態加載內容

有時候我們會需要在html裏面判斷當前用戶是否已經登錄,並根據結果展示不同內容。這裏以首頁爲例,未登錄用戶只展示“登錄”和“註冊”,已登錄用戶展示“登出”和“Profile”。
修改home.html

{% if user.is_authenticated %}
    <a href="{% url "logout" %}">Logout</a>
    <br>
    <a href="{% url "profile" %}">Profile</a>
{% else %}
    <a href="{% url "register" %}">Register</a>
    <br>
    <a href="{% url "login" %}">Login</a>
{% endif %}

我們可以直接通過調用user.is_authenticated來判斷當前用戶是否爲已登錄(已驗證)。

本文代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章