第六章:我的主頁和頭像

感謝做者 –> 原文連接

本文翻譯自 The Flask Mega-Tutorial Part VI: Profile Page and Avatarshtml

這是Flask Mega-Tutorial系列的第六部分,我將告訴你如何建立我的主頁。python

本章將致力於爲應用添加我的主頁。我的主頁用來展現用戶的相關信息,其我的信息由本人錄入。 我將爲你展現如何動態地生成每一個用戶的主頁,並提供一個編輯頁面給他們來更新我的信息。git

本章的GitHub連接爲:BrowseZipDiff.github

我的主頁

做爲建立我的主頁的第一步,讓咱們爲其URL /user/  新建一個對應的視圖函數。web

 
 
 
 

我用來裝飾該視圖函數的@app.route裝飾器看起來和以前的有點不同。 本例中被<>包裹的URL <username>是動態的。 當一個路由包含動態組件時,Flask將接受該部分URL中的任何文本,並將以實際文本做爲參數調用該視圖函數。 例如,若是客戶端瀏覽器請求URL /user/susan,則視圖函數將被調用,其參數username被設置爲'susan'。 由於這個視圖函數只能被已登陸的用戶訪問,因此我添加了@login_required裝飾器。數據庫

這個視圖函數的實現至關簡單。 我首先會嘗試在數據庫中以用戶名來查詢和加載用戶。 以前你見過經過調用all()來獲得全部的結果的查詢,或是調用first()來獲得結果中的第一個或者結果集爲空時返回None的查詢。 在本視圖函數中,我使用了first()的變種方法,名爲first_or_404(),當有結果時它的工做方式與first()徹底相同,可是在沒有結果的狀況下會自動發送404 error給客戶端。 以這種方式執行查詢,我省去檢查用戶是否返回的步驟,由於當用戶名不存在於數據庫中時,函數將不會返回,而是會引起404異常。flask

若是執行數據庫查詢沒有觸發404錯誤,那麼這意味着找到了具備給定用戶名的用戶。 接下來,我爲這個用戶初始化一個虛擬的用戶動態列表,最後用傳入的用戶對象和用戶動態列表渲染一個新的user.html模板。api

user.html模板以下所示:瀏覽器

 
 
 
 

我的主頁雖然已經完成了,可是網站上卻沒有一個入口連接。我將會在頂部的導航欄中添加這個入口連接,以便用戶能夠輕鬆查看本身的我的資料:服務器

 
 
 
 

這裏惟一有趣的變化是用來生成連接到我的主頁的url_for()調用。 因爲我的主頁視圖函數接受一個動態參數,因此url_for()函數接收一個值做爲關鍵字參數。 因爲這是一個指向當前登陸我的主頁的連接,我可使用Flask-Login的current_user對象來生成正確的URL。

我的主頁

嘗試點擊頂部的Profile連接就能將你帶到本身的我的主頁。 此時,雖然沒有連接來訪問其餘用戶的主頁,可是若是要訪問這些頁面,則能夠在瀏覽器的地址欄中手動輸入網址。 例如,若是你在應用中註冊了名爲「john」的用戶,則能夠經過在地址欄中鍵入http:// localhost:5000/user/john來查看該用戶的我的主頁。

頭像

我相信你也以爲我剛剛創建的我的主頁很是枯燥乏味。爲了使它們更加有趣,我將添加用戶頭像。與其在服務器上處理大量的上傳圖片,我將使用Gravatar爲全部用戶提供圖片服務。

Gravatar服務使用起來很是簡單。 要請求給定用戶的圖片,使用格式爲https://www.gravatar.com/avatar/ 的URL便可,其中<hash>是用戶的電子郵件地址的MD5哈希值。 在下面,你能夠看到如何生成電子郵件爲john@example.com的用戶的Gravatar URL:

 
 
 
 

若是你想看一個實際的例子,我本身的Gravatar URL是https://www.gravatar.com/avatar/729e26a2a2c7ff24a71958d4aa4e5f35。Gravatar返回的圖片以下:

Miguel的頭像

默認狀況下,返回的圖像大小是80x80像素,但能夠經過向URL的查詢字符串添加s參數來請求不一樣大小的圖片。 例如,要得到我本身128x128像素的頭像,該URL是https://www.gravatar.com/avatar/729e26a2a2c7ff24a71958d4aa4e5f35?s=128

另外一個可傳遞給Gravatar的有趣參數是d,它讓Gravatar爲沒有向服務註冊頭像的用戶提供的隨機頭像。 我最喜歡的隨機頭像類型是「identicon」,它爲每一個郵箱都返回一個漂亮且不重複的幾何設計圖片。 以下:

Identicon頭像

請注意,一些Web瀏覽器插件(如Ghostery)會屏蔽Gravatar圖像,由於它們認爲Automattic(Gravatar服務的全部者)能夠根據你發送的獲取頭像的請求來判斷你正在訪問的網站。 若是在瀏覽器中看不到頭像,你在排查問題的時候能夠考慮如下是否在瀏覽器中安裝了此類插件。

因爲頭像與用戶相關聯,因此將生成頭像URL的邏輯添加到用戶模型是有道理的。

 
 
 
 

User類新增的avatar()方法須要傳入需求頭像的像素大小,並返回用戶頭像圖片的URL。 對於沒有註冊頭像的用戶,將生成「identicon」類的隨機圖片。 爲了生成MD5哈希值,我首先將電子郵件轉換爲小寫,由於這是Gravatar服務所要求的。 而後,由於Python中的MD5的參數類型須要是字節而不是字符串,因此在將字符串傳遞給該函數以前,須要將字符串編碼爲字節。

若是你對Gravatar服務頗有興趣,能夠學習他們的文檔

下一步須要將頭像圖片插入到我的主頁的模板中:

 
 
 
 

使用User類來返回頭像URL的好處是,若是有一天我不想繼續使用Gravatar頭像了,我能夠重寫avatar()方法來返回其餘頭像服務網站的URL,全部的模板將自動顯示新的頭像。

個人我的主頁的頂部有一個不錯的大頭像,不止如此,底下的全部用戶動態都會有一個小頭像。 對於我的主頁而言,全部的頭像固然都是對應用戶的。我將會在主頁面上實現每一個用戶動態都用其做者的頭像來裝飾,這樣一來看起來就很是棒了。

爲了顯示用戶動態的頭像,我只須要在模板中進行一個小小的更改:

 
 
 
 

頭像

使用Jinja2子模板

我設計的我的主頁,使用頭像和文字組合的方式來展現了用戶動態。 如今我想在主頁也使用相似的風格來佈局。 我能夠複製/粘貼來處理用戶動態渲染的模板部分,但這實際上並不理想,由於以後若是我想要對此佈局進行更改,我將不得不記住要更新兩個模板。

取而代之,我要建立一個只渲染一條用戶動態的子模板,而後在user.htmlindex.html模板中引用它。 首先,我要建立這個只有一條用戶動態HTML元素的子模板。 我將其命名爲app/templates/_post.html _前綴只是一個命名約定,能夠幫助我識別哪些模板文件是子模板。

 
 
 
 

我在user.html模板中使用了Jinja2的include語句來調用該子模板:

 
 
 
 

應用的主頁尚未完善,因此如今我不打算在其中添加這個功能。

更多有趣的我的資料

新增的我的主頁存在的一個問題是,真正顯示的內容不夠豐富。 用戶喜歡在我的主頁上展現他們的相關信息,因此我會讓他們寫一些自我介紹並在這裏展現。 我也將跟蹤每一個用戶最後一次訪問該網站的時間,並顯示在他們的我的主頁上。

爲了支持全部這些額外的信息,首先須要作的是用兩個新的字段擴展數據庫中的用戶表:

 
 
 
 

每次數據庫被修改時,都須要生成數據庫遷移。 在第四章中,我向你展現瞭如何設置應用以經過遷移腳本跟蹤數據庫的變動。 如今有兩個新的字段我想添加到數據庫中,因此第一步是生成遷移腳本:

 
 
 
 

migrate命令的輸出表示一切正確運行,由於它顯示User類中的兩個新字段已被檢測到。 如今我能夠將此更改應用於數據庫:

 
 
 
 

我但願你認識到使用遷移框架是多麼有用。 數據庫中的用戶數據仍然存在,遷移框架如同實施手術教學般地精準執行遷移腳本中的更改而且不損壞任何數據。

下一步,我將會把新增的兩個字段增長到我的主頁中:

 
 
 
 

請注意,我用Jinja2的條件語句來封裝了這兩個字段,由於我只但願它們在設置後纔可見。 目前,全部用戶的這兩個字段都是空的,因此若是如今運行應用,則不會看到這些字段。

記錄用戶的最後訪問時間

讓咱們從更容易實現的last_seen字段開始。 我想要作的就是一旦某個用戶向服務器發送請求,就將當前時間寫入到這個字段。

爲每一個視圖函數添加更新這個字段的邏輯,這麼作很是的枯燥乏味。在視圖函數處理請求以前執行一段簡單的代碼邏輯在Web應用中十分常見,所以Flask提供了一個內置功能來實現它。解決方案以下:

 
 
 
 

Flask中的@before_request裝飾器註冊在視圖函數以前執行的函數。這是很是有用的,由於如今我能夠在一處地方編寫代碼,並讓它在任何視圖函數以前被執行。該代碼簡單地實現了檢查current_user是否已經登陸,並在已登陸的狀況下將last_seen字段設置爲當前時間。我以前提到過,應用應該以一致的時間單位工做,標準作法是使用UTC時區,使用系統的本地時間不是一個好主意,由於若是那麼的話,數據庫中存儲的時間取決於你的時區。最後一步是提交數據庫會話,以便將上面所作的更改寫入數據庫。若是你想知道爲何在提交以前沒有db.session.add(),考慮在引用current_user時,Flask-Login將調用用戶加載函數,該函數將運行一個數據庫查詢並將目標用戶添加到數據庫會話中。因此你能夠在這個函數中再次添加用戶,可是這不是必須的,由於它已經在那裏了。

若是在進行此更改後查看你的我的主頁,則會看到「Last seen on」行,而且時間很是接近當前時間。 若是你離開我的主頁,而後返回,你會看到時間在不斷更新。

事實上,我在存儲時間和在我的主頁顯示時間的時候,使用的都是UTC時區。 除此以外,顯示的時間格式也可能不是你所預期的,由於實際上它是Python datetime對象的內部表示。 如今,我不會操心這兩個問題,由於我將在後面的章節中討論在Web應用中處理日期和時間的主題。

最後訪問時間

我的資料編輯器

我還須要給用戶一個表單,讓他們輸入一些我的資料。 表單將容許用戶更改他們的用戶名,而且寫一些我的介紹,以存儲在新的about_me字段中。 讓咱們開始爲它寫一個表單類吧:

 
 
 
 

我在這個表單中使用了一個新的字段類型和一個新的驗證器。 對於「about_me」字段,我使用TextAreaField,這是一個多行輸入文本框,用戶能夠在其中輸入文本。 爲了驗證這個字段的長度,我使用了Length,它將確保輸入的文本在0到140個字符之間,由於這是我爲數據庫中的相應字段分配的空間。

該表單的渲染模板代碼以下:

 
 
 
 

最後一步,使用視圖函數將它們結合起來:

 
 
 
 

這個視圖函數處理表單的方式和其餘的視圖函數略有不一樣。若是validate_on_submit()返回True,我將表單中的數據複製到用戶對象中,而後將對象寫入數據庫。可是當validate_on_submit()返回False時,多是因爲兩個不一樣的緣由。這多是由於瀏覽器剛剛發送了一個GET請求,我須要經過提供表單模板的初始版原本響應。也多是這種狀況,瀏覽器發送帶有表單數據的POST請求,但該數據中的某些內容無效。對於該表單,我須要區別對待這兩種狀況。當第一次請求表單時,我用存儲在數據庫中的數據預填充字段,因此我須要作與提交相反的事情,那就是將存儲在用戶字段中的數據移動到表單中,這將確保這些表單字段具備用戶的當前數據。但在驗證錯誤的狀況下,我不想寫任何表單字段,由於它們已經由WTForms填充了。爲了區分這兩種狀況,我須要檢查request.method,若是它是GET,這是初始請求的狀況,若是是POST則是提交表單驗證失敗的狀況。

我的資料編輯器

我將我的資料編輯頁面的連接添加到我的主頁,以便用戶使用:

 
 
 
 

請注意我巧妙使用的條件,它確保在查看本身的我的主頁時出現編輯我的資料的連接,而在查看其餘人的我的主頁時不會出現。

我的主頁和編輯連接

相關文章
相關標籤/搜索