本教程為系列教程,為方便閱讀,每篇教程開頭列出全部教程的目錄索引:
使用者註冊就是建立使用者物件,將使用者的個人資訊儲存到資料庫裡。回顧一下 Django 的 MVT 經典開發流程,對使用者註冊功能來說,首先建立使用者模型(M),這一步我們已經完成了。編寫註冊檢視函式(V),並將為檢視函式繫結對應的 URL。編寫註冊模板(T),模板中提供一個登錄檔單給使用者。Django 使用者系統內建了登入、修改密碼、找回密碼等檢視,但是唯獨使用者註冊的檢視函式沒有提供,這一部分需要我們自己來寫。
編寫使用者登錄檔單
Django 已經內建了一個使用者登錄檔單:django.contrib.auth.forms.UserCreationForm,不過這個表單的一個小問題是它關聯的是 django 內建的 User 模型,從它的原始碼中可以看出:
class UserCreationForm(forms.ModelForm):
...
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}複製程式碼
問題就出在內部類 Meta
的 model
屬性,它的值對應的是 auth.User,因此無法用於我們自定義的 User 模型。好在表單實際上就是一個 Python 類,因此我們可以繼承它,對它做一點小小的修改就可以了。
表單的程式碼通常寫在 forms.py 檔案裡,因此在 users 應用下新建一個 forms.py 檔案用於存放表單程式碼,然後寫上如下程式碼:
users/forms.py
from django.contrib.auth.forms import UserCreationForm
from .models import User
class RegisterForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = ("username", "email")複製程式碼
UserCreationForm
的 Meta
內部類下的 model
屬性對應的是 auth.User 模型。而 RegisterForm
通過覆寫父類 model
屬性的值,將其改為 users.User。
此外 fields
用於指定表單的欄位,這些指定的欄位在模板中會被渲染成表單控制元件(即一些 <input>
等表單控制元件)。 UserCreationForm
中只指定了 fields = ("username",)
,即使用者名稱,此外還有兩個欄位密碼和確認密碼在 UserCreationForm
的屬性中指定。所以預設的表單渲染後只有使用者名稱(username)、密碼、確認密碼三個表單控制元件。我們還希望使用者註冊時提供郵箱地址,所以在 fields
中增加了 email 欄位。
注意:雖然 model
屬性的值都被指定為 User,但一個是 auth.User,另一個是 users.User。
編寫使用者註冊檢視函式
首先來分析一下注冊函式的邏輯。使用者在登錄檔單裡填寫註冊資訊,然後通過表單將這些資訊提交給伺服器。檢視函式從使用者提交的資料提取使用者的註冊資訊,然後驗證這些資料的合法性。如果資料合法,就新建一個使用者物件,將使用者的資料儲存到資料庫,否則就將錯誤資訊返回給使用者,提示使用者對提交的資訊進行修改。過程就是這麼簡單,下面是對應的程式碼(檢視函式的程式碼通常寫在 views.py 檔案裡):
users/views.py
from django.shortcuts import render, redirect
from .forms import RegisterForm
def register(request):
# 只有當請求為 POST 時,才表示使用者提交了註冊資訊
if request.method == 'POST':
# request.POST 是一個類字典資料結構,記錄了使用者提交的註冊資訊
# 這裡提交的就是使用者名稱(username)、密碼(password)、郵箱(email)
# 用這些資料例項化一個使用者登錄檔單
form = RegisterForm(request.POST)
# 驗證資料的合法性
if form.is_valid():
# 如果提交資料合法,呼叫表單的 save 方法將使用者資料儲存到資料庫
form.save()
# 註冊成功,跳轉回首頁
return redirect('/')
else:
# 請求不是 POST,表明使用者正在訪問註冊頁面,展示一個空的登錄檔單給使用者
form = RegisterForm()
# 渲染模板
# 如果使用者正在訪問註冊頁面,則渲染的是一個空的登錄檔單
# 如果使用者通過表單提交註冊資訊,但是資料驗證不合法,則渲染的是一個帶有錯誤資訊的表單
return render(request, 'users/register.html', context={'form': form})複製程式碼
注意以上檢視是處理表單的經典流程,即:
def form_process_view(request):
if request.method == 'POST':
# 請求為 POST,利用使用者提交的資料構造一個繫結了資料的表單
form = Form(request.POST)
if form.is_valid():
# 表單資料合法
# 進行其它處理...
# 跳轉
return redirect('/')
else:
# 請求不是 POST,構造一個空表單
form = Form()
# 渲染模板
# 如果不是 POST 請求,則渲染的是一個空的表單
# 如果使用者通過表單提交資料,但是資料驗證不合法,則渲染的是一個帶有錯誤資訊的表單
return render(request, 'template.html', context={'form': form})複製程式碼
以上邏輯程式碼稍加修改就可以應用於各種表單處理。
設定 URL 模式
檢視函式需要和對應的 URL 繫結,這樣當使用者訪問某個 URL 時,Django 才知道呼叫哪個檢視函式處理使用者請求。首先在 users 應用下新建一個 urls.py 檔案用於設定註冊檢視函式的 URL 模式。
users/urls.py
from django.conf.urls import url
from . import views
app_name = 'users'
urlpatterns = [
url(r'^register/', views.register, name='register'),
]複製程式碼
app_name = 'users'
為這個 urls 模組設定名稱空間。關於 URL 模式的設定如果不明白的話請參考相關基礎教程,這裡不再贅述。
接下來需要在工程的 urls.py 檔案裡包含 users 應用的 URL 模式。開啟 django_auth_example/ 目錄下的 urls.py 檔案,將 users.urls.py 包含進來:
django_auth_example/urls.py
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 別忘記在頂部引入 include 函式
url(r'^users/', include('users.urls')),
]複製程式碼
編寫註冊頁面模板
我們在檢視函式中渲染了 users/register.html,不過目前這個模板檔案還不存在,我們這就來建立它。我習慣喜歡將模板檔案放在專案根目錄(manage.py 所在目錄)的 templates/ 目錄下,然後在 templates/ 目錄下再新建各個和應用同名的資料夾,用於存放該應用下的模板檔案。當然模板放在哪裡是無關緊要的,具體視專案而定,只要通過配置模板路徑使 Django 能夠找到模板檔案即可。
設定模板目錄結構
按照我的習慣,先在專案根目錄(manage.py 所在目錄)新建一個 templates/ 目錄,然後在 templates/ 目錄下新建一個 users 目錄,用於存放 users 應用的相關模板檔案。然後在 users/ 目錄下新建一個 register.html 模板檔案(注意是 templates/ 下的 users/ 目錄,不是 users 應用目錄)。此時目錄結構變為:
django_auth_example/
manage.py
django_auth_example/
__init__.py
settings.py
urls.py
wsgi.py
templates/
users/
register.html複製程式碼
配置模板路徑
接著需要在 settings.py 裡設定 templates/ 所在路徑,在 settings.py 找到 TEMPLATES
選項,它的內容是這樣的:
django_auth_example/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]複製程式碼
其中 DIRS
就是設定模板的路徑,在 [] 中寫入 os.path.join(BASE_DIR, 'templates')
,即像下面這樣:
django_auth_example/settings.py
TEMPLATES = [
{
...
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
},
]複製程式碼
這裡 BASE_DIR
是 settings.py 在配置開頭前面定義的變數,記錄的是工程根目錄 django_auth_example/ 的值(注意是最外層的 django_auth_example/ 目錄)。在這個目錄下有模板檔案所在的目錄 templates/,於是利用os.path.join
把這兩個路徑連起來,構成完整的模板路徑,Django 就知道去這個路徑下面找我們的模板了。
渲染登錄檔單
接下來就是在 register.html 模板中渲染表單了,具體程式碼如下:
templates/users/register.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>註冊</title>
<link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
<style>
.errorlist {
color: red;
}
</style>
</head>
<body>
<div class="flex-center">
<div class="container">
<div class="flex-center">
<div class="unit-1-2 unit-1-on-mobile">
<h3>註冊</h3>
<form class="form" action="{% url 'users:register' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">註冊</button>
</form>
<div class="flex-center top-gap text-small">
<a href="login.html">已有賬號登入</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>複製程式碼
為了使註冊頁面更加美觀,我引入了 mobi.css 提供樣式支援。其它的程式碼請忽略,重點只關注表單部分:
<form class="form" action="{% url 'users:register' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">註冊</button>
</form>複製程式碼
在 Django 中使用表單,必須注意以下幾點:
- 設定表單的 action 屬性。這個例子中,表單的資料將提交給 URL /users/register/,然後 Django 呼叫對應的檢視函式
register
進行處理。這裡我們使用了 {% url %} 模板標籤,防止 URL 硬編碼。關於 {% url %} 模板標籤,可以看這篇文章中的介紹 部落格文章詳情頁。 - 設定表單的 method 屬性,通常提交 表單資料都是通過 post 方法提交。
- 在表單中加入 {% csrf_token %} 模板標籤。這個模板標籤的用途就是用於防止跨站請求偽造攻擊,提高網站的安全性。至於什麼是跨站請求偽造,感興趣的可以搜尋相關資料查閱。這裡只需記住只要使用了表單,一定要在表單中加 {% csrf_token %} 模板標籤,否則 Django 將不允許你提交表單資料。
接下來就是表單的控制元件部分。對錶單 form(這是一個模板變數,是 RegisterForm
的一個例項,我們在 register
檢視函式中將它傳遞給模板的。)進行迴圈就可以得到表單的各個控制元件:
- {{ field.label_tag }} 是相應控制元件的 label 標籤
- {{ field }} 是相應的表單控制元件
- {{ field.errors }} 是表單的錯誤(如果有的話)
- {{ field.help_text|safe }} 是控制元件相關的幫助資訊
例如 RegisterForm
表單有使用者名稱欄位,渲染的表單控制元件為:
<label for="id_username">使用者名稱:</label><!-- 對應 {{ field.label_tag }} -->
<input type="text" name="username" id="id_username" autofocus required maxlength="150" /><!-- 對應 {{ field }} -->
<p class="help text-small text-muted">必填。150個字元或者更少。包含字母,數字和僅有的@/./+/-/_符號。</p><!-- 對應 {{ field.help_text }} -->複製程式碼
你可以按 F12 看看錶單的原始碼,對比一下表單控制元件是哪一部分渲染而成的。這種表單渲染方式是一種比較通用的做法,你可以把它當做一個模板,稍作修改就可以應用與其它需要渲染表單的模板中。
OK,執行開發伺服器,訪問 http://127.0.0.1:8000/users/register/,可以看到渲染的使用者登錄檔單了。
你可以嘗試註冊一個使用者,或者嘗試故意輸錯一些資訊,看看錶單渲染的錯誤資訊是什麼樣的,比如我故意輸入兩次不同的密碼,得到一個錯誤資訊提示:
在 Admin 後臺檢視使用者是否註冊成功
如果表單資料沒有錯誤,提交表單後就會跳轉到首頁,由於我們沒有寫任何處理首頁的檢視函式,所以得到一個 404 錯誤。不過沒有關係,我麼你現在只關心使用者是否註冊成功。那麼怎麼檢視使用者是否已經註冊成功呢?可以去 Django Admin 後臺看看是否有使用者新註冊的資料。為了在 Admin 後臺檢視使用者資料,首先需要註冊使用者模型。開啟 users/admin.py 檔案,在裡面註冊 users.User 模型:
users/admin.py
from django.contrib import admin
from .models import User
admin.site.register(User)複製程式碼
為了進入後臺,還要建立一個超級管理員使用者,使用 python manage.py createsuperuser
建立一個管理員賬戶即可。如果你不知道怎麼建立,請參照 在 Django Admin 後臺釋出文章 中的說明。
瀏覽器輸入 http://127.0.0.1:8000/admin/,登入管理員賬戶,可以檢視到註冊的使用者資訊了,比如在我的後臺可以看到三個使用者:
其中有一個是使用 createsuperuser 命令建立的管理員賬戶,另外兩個是註冊的新使用者。
至此,註冊功能已經完成了。使用者註冊後就要登入,接下來就是如何提供使用者登入功能了。
總結
本教程的示例專案程式碼位於 GitHub:Django Auth Example。
如果遇到問題,請通過下面的方式尋求幫助。
- 在 Django 使用者認證系統:註冊 評論區留言。
- 在 Pythonzhcn 的 Django 版塊 釋出問題詳細描述的帖子。
更多 Django 相關教程,請訪問我的個人部落格:追夢人物的部落格。