django用戶認證系統——注冊3


用戶注冊就是創建用戶對象,將用戶的個人信息保存到數據庫里。回顧一下 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

如果遇到問題,請通過下面的方式尋求幫助。

  • 在下方評論區留言。
  • 將問題的詳細描述通過郵件發送到 djangostudyteam@163.com,一般會在 24 小時內回復。
  • 在 Pythonzhcn 社區的新手問答版塊 發布帖子。

更多 Django 相關教程,請訪問我的個人博客:追夢人物的博客


免責聲明!

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



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