flask入門的教程-(用戶信息頁和頭像) Profile Page And Avatars

文章轉自 :https://github.com/WapeYang/The-Flask-Mega-Tutorial/blob/master/profile.rst

感謝原作者的付出

轉載時間爲:2014-05-06


用戶信息頁和頭像

回顧

在上一章中,我們已經完成了登錄系統,因此我們可以使用 OpenIDs 登錄以及登出。

今天,我們將要完成個人信息頁。首先,我們將創建用戶信息頁,顯示用戶信息以及最近的 blog。作爲其中一部分,我們將會學習到顯示用戶頭像。接着,我們將要用戶 web 表單用來編輯用戶信息。

用戶信息頁

創建一個用戶信息不需要引入新的概念。我們只要創建一個新的視圖函數以及與它配套的 HTML 模版。

這裏就是視圖函數(文件 app/views.py):

@app.route('/user/<nickname>')
@login_required
def user(nickname):
    user = User.query.filter_by(nickname = nickname).first()
    if user == None:
        flash('User ' + nickname + ' not found.')
        return redirect(url_for('index'))
    posts = [
        { 'author': user, 'body': 'Test post #1' },
        { 'author': user, 'body': 'Test post #2' }
    ]
    return render_template('user.html',
        user = user,
        posts = posts)

我們用於這個視圖函數的裝飾器與之前的有些不同。在這個例子中,我們有一個 參數 在裏面,用 <nickname> 來表示。這轉化爲一個同名的參數添加到視圖函數。當客戶端以 URL /user/miguel 請求的時候,視圖函數收到一個 nickname = 'miguel' 參數而被調用。

視圖函數的實現沒有讓人驚喜的。首先,我們使用接收到參數 nickname 試着從數據庫載入用戶。如果沒有找到用戶的話,我們將會拋出錯誤信息,重定向到主頁。

一旦我們找到用戶,我們把它傳入到 render_template 調用, 並且傳入一些僞造的 blog。注意在用戶信息頁上只會顯示該用戶的 blog,因此,我們僞造的 blog 的 author 域必須正確。

我們最初的視圖模版是十分簡單的(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>User: {{user.nickname}}!</h1>
<hr>
{% for post in posts %}
<p>
  {{post.author.nickname}} says: <b>{{post.body}}</b>
</p>
{% endfor %}
{% endblock %}

用戶信息頁現在已經完成了,但是缺少對它的鏈接。爲了讓用戶很容易地檢查他的或者她的信息,我們直接把用戶信息頁的鏈接放在導航欄中(文件 app/templates/base.html):

<div>Microblog:
     <a href="{{ url_for('index') }}">Home</a>
     {% if g.user.is_authenticated() %}
     | <a href="{{ url_for('user', nickname = g.user.nickname) }}">Your Profile</a>
     | <a href="{{ url_for('logout') }}">Logout</a>
     {% endif %}
 </div>

試試應用程序吧。點擊導航欄中的個人資料鏈接,會把你帶到用戶信息頁。因爲我們還沒有到任何用戶的信息頁的鏈接,因此你必須手動鍵入你想要看到的用戶信息的 URL。比如,你可以鍵入 http://localhost:5000/user/miguel,查看 miguel 用戶信息。

頭像

我敢肯定你會同意我們的個人信息頁是很無聊的。爲了讓他們有點更有趣,讓我們添加用戶頭像。

不需要在我們自己的服務器處理大量的上傳圖片,我們依賴 Gravatar 服務爲我們提供用戶頭像。

因爲返回一個頭像是與用戶相關的任務,我們把它放在 User 類(文件 app/models.py):

from hashlib import md5
# ...
class User(db.Model):
    # ...
    def avatar(self, size):
        return 'http://www.gravatar.com/avatar/' + md5(self.email).hexdigest() + '?d=mm&s=' + str(size)

User 的方法 avatar 返回用戶圖片的 URL,以像素爲單位縮放成要求的尺寸。

有了 Gravatar 服務的協助,很容易處理頭像。你只需要創建一個用戶郵箱的 MD5 哈希,然後將其加入 URL中,像上面你看見的。在郵箱 MD5 後,你還需要提供一個定製頭像尺寸的數字。d=mm 決定什麼樣的圖片佔位符當用戶沒有 Gravatar 賬戶。mm 選項將會返回一個“神祕人”圖片,一個人灰色的輪廓。s=N 選項要求頭像按照以像素爲單位的給定尺寸縮放。

Gravatar 官方文檔 對 avatar URL 有着更加詳細的解釋。

現在我們的 User 類知道怎樣返回一個頭像圖片,我們把它融入到用戶信息頁的佈局中(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td><h1>User: {{user.nickname}}</h1></td>
    </tr>
</table>
<hr>
{% for post in posts %}
<p>
  {{post.author.nickname}} says: <b>{{post.body}}</b>
</p>
{% endfor %}
{% endblock %}

User 類負責返回頭像是一個很巧妙的事情,如果有一天決定不想要 Gravatar 頭像,我們只要重構 avatar 返回不同的 URLs(即使指向我們自己的服務器,如果我們想要自己的頭像服務器),所有我們的模版將會自動地開始顯示新的頭像。

我們已經在用戶信息頁上添加了頭像,如果我們想要在每一個 blog 前面顯示頭像了?這也是一個簡單的工作,爲了在每一個 blog 前顯示頭像,我們只需要在模塊做一些小改變(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td><h1>User: {{user.nickname}}</h1></td>
    </tr>
</table>
<hr>
{% for post in posts %}
<table>
    <tr valign="top">
        <td><img src="{{post.author.avatar(50)}}"></td><td><i>{{post.author.nickname}} says:</i><br>{{post.body}}</td>
    </tr>
</table>
{% endfor %}
{% endblock %}

這就是我們的用戶資料頁的樣子:

images/4.png

在子模板中重用

我們已經實現了用戶信息頁,它能夠顯示用戶的 blog。我們的首頁也應該顯示任何一個用戶這個時候的 blog 。這樣我們有兩個頁需要顯示用戶的 blog。當然我們可以直接拷貝和複製處理渲染 blog 的模板,但這不是最理想的。因爲當我們決定要修改 blog 的佈局的時候,我們要更新所有使用它的模板。

相反,我們將要製作一個渲染 blog 的子模板,我們在使用它的模板中包含這個子模板。

我們創建一個 blog 的子模板,這是一個再普通不過的模板(文件 /app/templates/post.html):

<table>
    <tr valign="top">
        <td><img src="{{post.author.avatar(50)}}"></td><td><i>{{post.author.nickname}} says:</i><br>{{post.body}}</td>
    </tr>
</table>

接着我們使用 Jinja2 的 include 命令在我們的用戶模板中調用這個子模板(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td><h1>User: {{user.nickname}}</h1></td>
    </tr>
</table>
<hr>
{% for post in posts %}
    {% include 'post.html' %}
{% endfor %}
{% endblock %}

一旦我們有一個功能上完全實現的首頁,我們將會調用這個子模板,但是現在不準備這麼做,將會把它留在後面的章節。

更多有趣的信息

盡然我們現在已經有一個不錯的用戶信息頁,我們還有更多的信息需要在上面顯示。像用戶自我說明可以顯示在用戶信息頁上,因此我們將會讓用戶寫一些自我介紹,並將它們顯示在用戶資料頁上。我們也將追蹤每個用戶訪問頁面的最後一次的時間,因此我們將會把它顯示在用戶信息頁上。

爲了增加這些,我們必須開始修改數據庫。更具體地說,我們必須在我們的 User 類上增加兩個字段(文件 app/models.py):

class User(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    nickname = db.Column(db.String(64), unique = True)
    email = db.Column(db.String(120), index = True, unique = True)
    role = db.Column(db.SmallInteger, default = ROLE_USER)
    posts = db.relationship('Post', backref = 'author', lazy = 'dynamic')
    about_me = db.Column(db.String(140))
    last_seen = db.Column(db.DateTime)

前面的章節我們已經講述過數據庫的遷移。因此爲了增加這兩個新字段到數據庫,需要運行升級腳本:

./db_migrate.py

腳本會返回如下信息:

New migration saved as db_repository/versions/003_migration.py
Current database version: 3

我們的兩個新字段加入到我們的數據庫。記得如果在 Windows 上的話,調用腳本的方式不同。

如果我們沒有遷移的支持,我們必須手動地編輯數據庫,最差的方式就是刪除表再重新創建。

接着,讓我們修改用戶信息頁模板來展示這些字段(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td>
            <h1>User: {{user.nickname}}</h1>
            {% if user.about_me %}<p>{{user.about_me}}</p>{% endif %}
            {% if user.last_seen %}<p><i>Last seen on: {{user.last_seen}}</i></p>{% endif %}
        </td>
    </tr>
</table>
<hr>
{% for post in posts %}
    {% include 'post.html' %}
{% endfor %}
{% endblock %}

注意:我們利用 Jinja2 的條件語句來顯示這些字段,因爲只有當它們被設置的時候纔會顯示出來。

last_seen 字段能夠被聰明地支持。記得在之前的章節中,我們創建了一個 before_request 函數,用來註冊登錄的用戶到全局變量flask.g 中。這個函數可以用來在數據庫中更新用戶最後一次的訪問時間(文件 app/views.py):

from datetime import datetime
# ...
@app.before_request
def before_request():
    g.user = current_user
    if g.user.is_authenticated():
        g.user.last_seen = datetime.utcnow()
        db.session.add(g.user)
        db.session.commit()

如果你登錄到你的信息頁,最後出現時間會顯示出來。每次刷新頁面,最後出現時間都會更新,因此每次瀏覽器在發送請求之前,before_request 函數都會在數據庫中更新時間。

注意的是我們是以標準的 UTC 時區寫入時間。我們在之前的章節中討論過這個問題,因此我們將會以 UTC 格式寫入所有時間內容以保證它們的一致性。這種時間形式在前臺顯示,看起來會很彆扭。我們將會在後面的章節中修正這種顯示問題。

要顯示用戶的關於我的信息,我們必須給他們輸入的地方,在“編輯個人信息”頁面,這是正確的地方。

編輯用戶信息

新增一個用戶信息表單是相當容易的。我們開始創建網頁表單(文件 app/forms.py):

from flask.ext.wtf import Form, TextField, BooleanField, TextAreaField
from flask.ext.wtf import Required, Length

class EditForm(Form):
    nickname = TextField('nickname', validators = [Required()])
    about_me = TextAreaField('about_me', validators = [Length(min = 0, max = 140)])

接着視圖模板(文件 app/templates/edit.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>Edit Your Profile</h1>
<form action="" method="post" name="edit">
    {{form.hidden_tag()}}
    <table>
        <tr>
            <td>Your nickname:</td>
            <td>{{form.nickname(size = 24)}}</td>
        </tr>
        <tr>
            <td>About yourself:</td>
            <td>{{form.about_me(cols = 32, rows = 4)}}</td>
        </tr>
        <tr>
            <td></td>
            <td><input type="submit" value="Save Changes"></td>
        </tr>
    </table>
</form>
{% endblock %}

最後我們編寫視圖函數(文件 app/views.py):

from forms import LoginForm, EditForm

@app.route('/edit', methods = ['GET', 'POST'])
@login_required
def edit():
    form = EditForm()
    if form.validate_on_submit():
        g.user.nickname = form.nickname.data
        g.user.about_me = form.about_me.data
        db.session.add(g.user)
        db.session.commit()
        flash('Your changes have been saved.')
        return redirect(url_for('edit'))
    else:
        form.nickname.data = g.user.nickname
        form.about_me.data = g.user.about_me
    return render_template('edit.html',
        form = form)

爲了能夠讓這頁很容易訪問到,我們在用戶信息頁上添加了一個鏈接(文件 app/templates/user.html):

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<table>
    <tr valign="top">
        <td><img src="{{user.avatar(128)}}"></td>
        <td>
            <h1>User: {{user.nickname}}</h1>
            {% if user.about_me %}<p>{{user.about_me}}</p>{% endif %}
            {% if user.last_seen %}<p><i>Last seen on: {{user.last_seen}}</i></p>{% endif %}
            {% if user.id == g.user.id %}<p><a href="{{url_for('edit')}}">Edit</a></p>{% endif %}
        </td>
    </tr>
</table>
<hr>
{% for post in posts %}
    {% include 'post.html' %}
{% endfor %}
{% endblock %}

編輯用戶信息的鏈接是十分智能的,只有當用戶瀏覽自己的用戶信息頁的時候纔會出現,瀏覽其他用戶的時候是不會出現的。

下面用戶信息頁的新的截圖:

images/5.png

結束語

最後留給大家一個問題,應用程序存在一個 bug。這個問題在前面的章節就已經存在,這一章的代碼存在同樣的問題。在下一章中我會解釋這個 bug,並且修正它。

如果你想要節省時間的話,你可以下載 microblog-0.6.zip

請記住數據庫並不包含在上述的壓縮包中,請使用 db_upgrade.py 升級數據庫,用 db_create.py 創建新的數據庫。

我希望能在下一章繼續見到各位!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章