本文為 Django 會員系統建立教學,第 3 篇:
- 設計登入與註冊功能的基礎路由與頁面配置
- Django 使用者密碼加密方式詳解
- 使用 Django 內建功能實現使用者註冊與登入 👈 所在位置
- 使用 Django 實現安全登出功能:完整指南
- Django 使用者登入與資料操作的最佳實踐
- Django: 添加公開留言與履歷列表功能
- 使用 Alpine.js 和 Django 動態管理留言按鈕啟用狀態
- Django 收藏功能的實現:使用 ManyToMany 關係與自定義中介模型
建議閱讀本文前,先閱讀完 Django 與前端框架教學 系列文
Django 提供了一套強大的認證與使用者管理功能,可以輕鬆實現安全的使用者註冊、登入。
本文將詳細介紹如何使用 Django 的內建功能搭建基礎使用者系統。
使用內建的 UserCreationForm 進行註冊
Django 提供了一個方便的 UserCreationForm 類,它基於 ModelForm,幫助我們快速處理使用者註冊表單,包括表單驗證和密碼加密。
註冊視圖(register)
在 user/views.py 中新增註冊功能:
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib import messages
def register(request):
if request.method == "POST":
form = UserCreationForm(request.POST) # 將表單與 POST 數據綁定
if form.is_valid(): # 驗證表單數據
form.save() # 保存使用者資料
messages.success(request, "註冊成功!")
return redirect("pages:home") # 重定向至首頁
else:
form = UserCreationForm() # 創建空白表單
return render(request, "users/register.html", {"form": form})
UserCreationForm 的用法解析
UserCreationForm的來源與功能:- 它是 Django 提供的一個表單類,專為使用者註冊設計。
- 繼承了
ModelForm,直接基於User模型生成欄位。
form的資料流:- 資料來源:
request.POST包含表單提交的數據,這些數據通過form = UserCreationForm(request.POST)綁定至表單物件。 - 驗證:
form.is_valid()會檢查必填欄位、密碼一致性等條件。 - 保存:
form.save()會自動將驗證通過的數據保存到資料庫中,創建新的User實例。
- 資料來源:
- 保存過程:
- 使用內建的
UserManager.create_user()方法,將密碼加密後儲存,確保安全性。
- 使用內建的
註冊模板(register.html)
在 users/templates/users/register.html 中設計註冊表單:
{% extends "shared/layout.html" %}
{% block "content" %}
<h1 class="text-3xl">註冊</h1>
<form method="POST" class="flex flex-col gap-2">
{% csrf_token %}
<div class="form-control">
<label>Username</label>
<input type="text" class="w-full max-w-xs input input-bordered" name="username" />
</div>
<div class="form-control">
<label>Email</label>
<input type="email" class="w-full max-w-xs input input-bordered" name="email" />
</div>
<div class="form-control">
<label>Password</label>
<input type="password" class="w-full max-w-xs input input-bordered" name="password1" />
</div>
<div class="form-control">
<label>Password 確認</label>
<input type="password" class="w-full max-w-xs input input-bordered" name="password2" />
</div>
<div>
<button class="btn btn-primary">註冊</button>
</div>
</form>
{% endblock %}表單輸入欄位的設計依據
此表單的欄位是根據 Django 提供的 UserCreationForm 類推敲出來的,其欄位對應如下:
username: 對應UserCreationForm中的username欄位,為使用者名的輸入框。email: 雖然UserCreationForm預設不包含電子郵件欄位,但可自訂擴展,此處用於記錄使用者的電子郵件地址。password1: 對應UserCreationForm的第一個密碼輸入欄位,用於輸入密碼。password2: 對應UserCreationForm的第二個密碼輸入欄位,用於確認密碼。
UserCreationForm 是基於 Django 的 User 模型,它預設包含了使用者名與密碼相關的欄位,並自動檢查兩次密碼是否一致。
使用內建的 authenticate 進行登入驗證
Django 的 authenticate 方法用於驗證使用者的憑據。
驗證成功後,我們還需要使用 login 函數為使用者創建 cookie(類似分發號碼牌)。
登入視圖(login)
在 user/views.py 中實現登入功能:
from django.contrib.auth import authenticate, login as login_user
from django.contrib import messages
from django.shortcuts import render, redirect
def login(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
user = authenticate(request, username=username, password=password) # 驗證使用者
if user is not None:
login_user(request, user) # 為使用者創建 cookie
messages.success(request, "登入成功!")
return redirect("pages:home")
else:
messages.error(request, "登入失敗,請檢查您的使用者名與密碼。")
return render(request, "users/login.html")authenticate 方法詳解
- 功能:
authenticate()是 Django 的內建方法,用於驗證使用者的憑證(如使用者名與密碼)。- 它會檢查資料庫中的使用者,並確認密碼是否正確。
- 返回值:
- 驗證成功:返回
User物件。 - 驗證失敗:返回
None。
- 驗證成功:返回
login 函數的作用
- 功能:
login()是 Django 的另一個內建方法,用於在伺服器端為使用者創建 cookie(類似於發號碼牌)。- 一旦使用者登入,Django 會在瀏覽器的 cookie 中設置 cookie 資訊。
- 撞名問題:
- 我們的函數名稱與 Django 的
login函數衝突,因此在導入時將其重命名為login_user:from django.contrib.auth import login as login_user。
- 我們的函數名稱與 Django 的
登入過程解析
- 驗證階段:
- 使用
authenticate()檢查使用者名與密碼是否有效。
- 使用
- 發號碼牌:
- 使用
login()為使用者創建 cookie,設置 cookie,表示使用者已登入。
- 使用
- 完整過程:
- 無
login():即使驗證成功,使用者依然未登入,因為未建立 cookie。
- 無
登入模板(login.html)
在 templates/users/login.html 中設計登入表單:
{% extends "shared/layout.html" %}
{% block "content" %}
<h1 class="text-3xl">登入</h1>
<form method="POST" class="flex flex-col gap-2">
{% csrf_token %}
<div class="form-control">
<label>Username</label>
<input type="text" class="w-full max-w-xs input input-bordered" name="username" />
</div>
<div class="form-control">
<label>Password</label>
<input type="password" class="w-full max-w-xs input input-bordered" name="password" />
</div>
<div>
<button class="btn btn-primary">登入</button>
</div>
</form>
{% endblock %}
在導覽列中動態顯示登入狀態
修改 templates/shared/navbar.html,根據使用者的登入狀態顯示不同選項:
<div class="navbar bg-neutral text-neutral-content">
<div class="flex-1">
<a href="{% url 'pages:home' %}" class="text-xl btn btn-ghost">PyDev</a>
</div>
<div class="flex-none">
<ul class="px-1 menu menu-horizontal">
{% if user.is_authenticated %}
<li><a>{{ user.username }}</a></li>
<li><a href="{% url 'users:logout' %}">登出</a></li>
{% else %}
<li><a href="{% url 'users:login' %}">登入</a></li>
<li><a href="{% url 'users:register' %}">註冊</a></li>
{% endif %}
</ul>
</div>
</div>
說明
{% if user.is_authenticated %}的作用:- 該段模板代碼用來判斷當前使用者是否已通過身份驗證。
- 如果
user.is_authenticated為True,代表使用者已登入,顯示「使用者名」與「登出」選項。 - 若為
False,則顯示「登入」與「註冊」選項。
- 為什麼模板中可以直接取得
user變數:- Django 的
RequestContext預設會將當前請求的使用者變數(request.user)自動添加到模板上下文中。 - 無需在
views.py中手動傳入user,只需啟用django.template.context_processors.auth(預設啟用)。
- Django 的
is_authenticated屬性的來源:is_authenticated是AbstractBaseUser類的屬性,表示使用者是否已登入。- 它的值與使用者的身份驗證狀態相關,並由
django.contrib.auth中的中介軟體(Middleware)負責處理。
- 與
views.py的關聯:views.py不直接操作is_authenticated。它與 Django 的認證中介軟體有關,例如AuthenticationMiddleware。- 當使用者登入後,Django 的 cookie 機制會將使用者的認證狀態存儲於伺服器端並更新
request.user。
完整的註冊與登入流程
- 使用者通過 註冊表單 提交資料,
UserCreationForm進行驗證並加密密碼。 - 使用者通過 登入表單 提交憑據,
authenticate驗證身份並使用login創建 cookie。 - 導覽列根據使用者的登入狀態動態顯示「登入/註冊」或「登出/個人信息」。
- 使用者可以隨時點擊「登出」來清除 cookie。
這套流程充分利用了 Django 的內建功能,不僅安全可靠,也非常易於實現和擴展。