Python學習之路18-用戶賬戶

本系列是對入門書籍《Python編程:從入門到實踐》的筆記整理,屬於初級內容。標題順序採用書中標題。

本篇記錄如何創建用戶註冊系統,如何實現用戶輸入自己的數據。

1. 前言

在本篇中,我們將:

  • 實現一個身份驗證系統。

2. 讓用戶能夠輸入數據

2.1 添加新主題

和之前創建網頁的步驟一樣:定義URL,編寫視圖函數,編寫模板。主要區別是,這裡需要一個包含表單的模塊forms.py

2.1.1 創建forms.py模塊

用戶輸入信息時,需要進行驗證,確保提交的信息是正確的數據類型,且不是惡意信息,如中斷服務器的代碼。然後再處理信息,並保存到數據庫中。當然,這些工作很多都由Django自動完成。

在models.py所在的目錄中新建forms.py模塊。創建表單的最簡單方法是繼承Django的ModelForm類:

Python學習之路18-用戶賬戶

最簡單的ModelForm版本只包含一個內嵌的Meta類,它告訴Django根據哪個模型創建表單,以及在表單中包含哪些字段。第6行,我們根據Topic創建一個表單,該表單只包含字段text(第7行),並不為該字段生成標籤(第8行)。

2.1.2 URL模式new_topic

當用戶要添加新主題時,將切換到http://localhost:8000/new_topic/ 。在learning_logs/urls.py中添加如下代碼:

Python學習之路18-用戶賬戶

2.1.3 視圖函數new_topic()

該函數需要處理兩種情形:①剛進入new_topic網頁,顯示一個空表單;②對提交的表單數據進行處理,並將用戶重定向到網頁topics。修改views.py文件:

Python學習之路18-用戶賬戶

2.1.4 GET請求和POST請求

創建Web應用程序時,將用到兩種主要數據請求類型:GET請求和POST請求。從這倆英文單詞可以看出,如果只從服務器讀取數據頁面,則使用GET請求;如果要提交用戶填寫的表單,通常使用POST請求。當然還有一些其他的請求類型,但這個項目中沒有使用。本項目中處理表單都使用POST方法。

request.method存儲了請求的類型(第7行代碼)。

當不是POST請求時,我們生成一個空表單傳遞給模板new_topic.html,然後返回給用戶;當請求是POST時,我們從request.POST這個變量中獲取用戶提交的數據,並暫存到form變量中。

通過is_valid()方法驗證表單數據是否滿足要求:用戶是否填寫了所有必不可少的字段(表單字段默認都是必填的),且輸入的數據與字段類型是否一致。當然這些驗證都是Django自動進行的。如果表單有效,在通過form的save()方法存儲到數據庫,然後通過reverse()函數獲取頁面topics的URL,並將其傳遞給HTTPResponseRedirect()以重定向到topics頁面。如果表單無效,把這些數據重新傳回給用戶。

2.1.5 模板new_topic.html

Python學習之路18-用戶賬戶

模板繼承了base.html,因此其基本結構和項目中的其他頁面相同。第6行中,參數action告訴服務器將提交的表單數據送到什麼位置去處理,參數method讓瀏覽器以POST請求的方式提交數據。

Django顯示錶單非常方便:只需要使用模板變量{{ form.as_p }},修飾符as_p讓Django以段落格式渲染所有表單元素,這是一種整潔地顯示錶單的簡單方法。

Django不自動創建提交表單的按鈕,需自行創建。

2.1.6 鏈接到頁面new_topic

在頁面topics.html中添加一個到頁面new_topic的鏈接:

Python學習之路18-用戶賬戶

2.1.7 效果

以下是實際效果圖:

Python學習之路18-用戶賬戶

通過這個頁面,隨意添加幾個主題,如下:

Python學習之路18-用戶賬戶

2.2 添加新條目

和前面的步驟相似:創建條目表單,添加URL,添加視圖,添加模板,鏈接到頁面

2.2.1 創建條目表單

創建一個與模型Entry相關聯的表單,但這個表單的自定義程度比TopicForm要高些,依然是在剛才創建的forms.py中添加:

Python學習之路18-用戶賬戶

代碼中定義了屬性widgets。小部件(widget)是一個HTML表單元素,如單行文本框、多行文本框或下拉列表。通過設置屬性widgets可以覆蓋Django選擇的默認小部件。通過Django的forms.Textarea定製字段“text”的輸入小部件,將文本框的寬度設置為80列,而不是默認的40列。

2.2.2 添加URL模式new_entry

修改learning_logs/urls.py:

Python學習之路18-用戶賬戶

該URL模式與形式為http://localhost:8000/new_entry/topi_id/ 的URL匹配,其中topic_id是主題的ID。

2.2.3 視圖函數new_entry()

與函數new_topic()很像:

Python學習之路18-用戶賬戶

new_entry()的定義包含形參topic_id,用於存儲從URL中獲得的值。

在調用save()時傳遞了參數commit=False(第14行),它讓Django創建一個新的條目對象,但並不立刻提交數據庫,而是暫時存儲在變量new_entry中,待為這個新條目對象添加了屬性topic之後再提交數據庫。

在重定向時,reverse()函數中傳遞了兩個參數,URL模式的名稱以及列表args,args包含要包含在URL中的所有參數。

2.2.4 模板new_entry.html

類似於new_topic:

Python學習之路18-用戶賬戶

注意第4行代碼,改行代碼返回到特定主題頁面。

2.2.5 鏈接到頁面new_entry

在顯示特定主題的頁面中添加到頁面new_entry的鏈接,修改topic.html:

Python學習之路18-用戶賬戶

2.2.6 效果

下圖是實際效果,請隨意添加一些條目:

Python學習之路18-用戶賬戶

2.3.1 URL模式edit_entry

修改learning_logs/urls.py:

Python學習之路18-用戶賬戶

2.3.2 視圖函數edit_entry()

Python學習之路18-用戶賬戶

首先獲取要被修改的entry以及與該條目相關的主題。處理GET請求時,通過參數instance=entry創建EntryForm實例,該參數讓Django創建一個表單,並使用既有條目對象中的信息填充它。處理POST請求時,還傳入了data=request.POST參數,Django根據POST中的相關數據對entry進行修改。

2.3.3 模板edit_entry.html

Python學習之路18-用戶賬戶

2.3.4 鏈接到頁面edit_entry.html

在顯示特定主題的頁面中,需要給每個條目添加到頁面edit_entry.html的鏈接,為此,修改topic.html:

Python學習之路18-用戶賬戶

2.3.5 效果

以下是實際效果圖:

Python學習之路18-用戶賬戶

3. 創建用戶賬戶

現在開始建立一個用戶註冊和身份驗證系統。為此將創建一個新的應用程序,其中包含處理用戶賬戶相關的所有功能。對Topic模型也要做稍許修改,讓每個主題都歸屬於特定用戶。

3.1 創建應用程序users

希望大家還記得如何使用startapp命令還創建APP:

Python學習之路18-用戶賬戶

將APP添加到settings.py中

Python學習之路18-用戶賬戶

在APP根目錄下創建urls.py文件,並添加命名空間:

Python學習之路18-用戶賬戶

為APP定義URL,修改項目根目錄中的urls.py:

Python學習之路18-用戶賬戶

3.2 登陸頁面

使用Django提供的默認登陸視圖,URL模式會有所不同。在users中的urls.py中添加如下代碼:

Python學習之路18-用戶賬戶

代碼中,我們使用Django自帶的login視圖函數(注意,參數是login,而不是views.login)。從之前的例子可以看出,我們渲染模板的代碼都是在自己寫的視圖函數中。但這裡使用了自帶的視圖函數,無法自行編寫進行渲染的代碼。所以,我們還傳了一個字典給path,告訴Django到哪裡查找我們要用到的模板。注意,該模板在users中,而不是在learning_logs中。

3.2.1 新建模板login.html

在learning_log/users/templates/users中創建login.html:

Python學習之路18-用戶賬戶

如果表單的errors屬性被設置,則顯示一條提示賬號密碼錯誤的信息。

3.2.2 鏈接到登陸頁面

在base.html中添加到登陸頁面的鏈接,讓所有頁面都包含它。將這個鏈接嵌套在一個 if 標籤中,用戶已登錄時隱藏掉該鏈接:

Python學習之路18-用戶賬戶

在Django身份驗證系統中,每個模板都可使用變量user,這個變量有一個is_authenticated屬性:如果用戶已登錄,該屬性將為True,否則為False。

3.2.3 使用登陸頁面

首先訪問localhost:8000/admin註銷超級用戶,再訪問localhost:8000/users/login/,得到如下頁面:

Python學習之路18-用戶賬戶

3.3 註銷

並不為註銷創建單獨的頁面,而是讓用戶單擊一個連接用於註銷並返回主頁。因此,需要做如下工作:註銷URL模式,新建視圖,鏈接到註銷視圖

在users/urls.py中添加與http://localhost:8000/users/logout/ 匹配的URL模式:

Python學習之路18-用戶賬戶

編寫視圖函數logout_view(),其中,直接調用Django自帶的logout()函數,該函數要求request作為參數:

Python學習之路18-用戶賬戶

在base.html中添加註銷鏈接:

Python學習之路18-用戶賬戶

3.4 註冊頁面

使用Django提供的表單UserCreationFrom,但編寫自己的視圖函數和模板。URL->view->template->link。

首先,創建註冊頁面的URL模式,修改users/urls.py:

Python學習之路18-用戶賬戶

其次,創建視圖register()

Python學習之路18-用戶賬戶

以上代碼在用戶成功創建了用戶後會自動登陸,該功能由login()函數實現。該函數將會為通過了身份驗證的用戶對象創建會話(session)。最後上述代碼重定向到主頁。

然後,編寫註冊頁面的模板register.html

Python學習之路18-用戶賬戶

最後,在頁面中顯示註冊鏈接,修改base.html,在用戶沒有登錄時顯示註冊鏈接:

Python學習之路18-用戶賬戶

下面是實際效果:

Python學習之路18-用戶賬戶

這是直接點register按鈕時的反饋,不過這裡有點疑惑,從上面的register.html中看到,其實代碼很簡單,但這裡有個浮動效果,而且在註冊模板中並沒有像前面那樣的{% form.errors %}模板標籤,但依然有未註冊成功時的反應,而且註冊的視圖函數也是自己寫的,並不是用的自帶的註冊函數,所以不知道是不是和form.as_p有關。之後再慢慢研究吧,

4. 讓用戶擁有自己的數據

用戶應該能夠輸入其專有的數據,所以應該創建一個系統,確定各項數據所屬的用戶,再限制對頁面的訪問,使得用戶只能使用自己的數據,即訪問控制。

4.1 使用@login_required限制訪問

Django提供了裝飾器@login_required,使得能輕鬆實現用戶只能訪問自己能訪問的頁面。

限制對topics.html的訪問

每個主題都歸特定用戶所有,所以需要加限制,修改learning_logs/views.py:

Python學習之路18-用戶賬戶

裝飾器也是一個函數,python在運行topics()前會先運行login_required()的代碼。

login_required()函數檢查用戶是否登錄,僅當用戶已登錄時,Django才運行topics()函數,若未登錄,就重定向到登陸界面。而為了實現這個重定向,還需要修改項目的settings.py文件,在該文件中添加這樣一個常量(其實也是變量),一般在文件末尾添加:

Python學習之路18-用戶賬戶

全面限制對項目“學習筆記”的訪問

Django能輕鬆地限制對頁面的訪問,但得自己設計需要限制哪些頁面。一般先確定哪些頁面不需要保護,再限制對其他頁面的訪問。在該項目中,我們不限制對主頁、註冊頁面和註銷鏈接的訪問,其他頁面均限制。在learning_logs/views.py中,除了index()外,每個視圖函數都加上@login_required。

4.2 將數據關聯到用戶

為了禁止用戶訪問其他用戶的數據,需要將用戶與數據關聯。只需要將最高層的數據關聯到用戶,這樣更低層的數據將自動關聯到用戶。下面修改Topic模型和相關視圖:

Python學習之路18-用戶賬戶

修改模型後,還需要遷移數據庫。此時,需要將主題與用戶關聯。這裡並沒有通過代碼進行關聯,我們在遷移數據庫時手動進行關聯。為此,我們需要先知道有哪些用戶,以及這些用戶的ID。我們通過Django shell查詢用戶信息,當然也可以直接查看數據庫,這裡不再演示。我們將主題都關聯到超級用戶ll_admin上,它的ID是1。現在我們執行數據遷移:

Python學習之路18-用戶賬戶

Django指出試圖給既有模型Topic添加一個必不可少(不可為空)的字段,而該字段沒有默認值,需要我們採取措施:要麼現在提供默認值,要麼退出並在models.py中添加默認值。我們選擇了直接輸入默認值。接下來,Django使用這個值來遷移數據庫,並生成了遷移文件0003_topic_owner.py,它在模型Topic中添加字段owner。

現在執行遷移命令:

Python學習之路18-用戶賬戶

執行後可以在Django shell中驗證是否遷移成功,這裡不再驗證。

4.3 只允許用戶訪問自己的主題

目前不管以哪個用戶身份登錄,都能看到所有主題。現在我們添加限制,讓用戶只能看到自己的主題。在views.py中,對topics()做如下修改:

Python學習之路18-用戶賬戶

用戶登錄後,request對象將有一個user屬性,這個屬性存儲了有關該用戶的信息。第5行代碼讓Django只從數據庫中讀取特定用戶的數據。

4.4 保護用戶的主題

上述代碼做到了登錄後只顯示相應用戶的數據,但是,如果登錄後直接通過URL訪問,如直接輸入http://localhost:8000/topics/1/ ,依然可以訪問不屬於自己的特定主題頁面。下面修改views.py中的topic()函數來加以限制:

Python學習之路18-用戶賬戶

4.5 保護頁面edit_entry

此時用戶也可以像上面一樣,登陸後直接通過URL來訪問edit_entry.html,現在我們對這個頁面也加以限制:

Python學習之路18-用戶賬戶

4.6 最後一步:將新主題關聯到當前用戶

當前用於添加新主題的頁面存在問題,因為它沒有將新主題關聯到特定用戶。如果此時嘗試添加新主題,將看到錯誤信息IntegrityError,指出learning_logs_topic.user_id不能為NULL,下面修改new_topic()函數:

Python學習之路18-用戶賬戶

現在,這個項目允許任何用戶註冊,而每個用戶想添加多少新主題都可以,每個用戶只能訪問自己的數據,無論是查看數據、輸入新數據還是修改舊數據時都是如此。

5. 小結


分享到:


相關文章: