評論在數據庫中的表示
評論和博客文章沒有太大區別,都有正文、作者和時間戳,而且在這個特定實現中都使用Markdown語法編寫,下表是comments表的圖集以及和其他數據表之間的關係
評論屬於某篇博客文章,因此定義了一個從posts表到comments表的一對多關係,使用這個關係可以獲取某篇特定博客文章的評論列表
comments表還和users表之間有一對多關係,通過這個關係可以獲取用戶發表的所有評論,還能間接知道用戶發表了多少篇評論,用戶發表的評論數量可以顯示在用戶資料頁中,Comment模型的定義如下
# app/models.py
#...
class Comment(db.Model):
__tablename__ = 'comments'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
body_html = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True ,default=datetime.utcnow)
disabled = db.Column(db.Boolean)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
post_id = db.Column(db.Integer, db.ForeignKey('posts.id'))
@staticmethod
def on_changed_body(target, value, oldvalue, initiator):
allowed_tags = ['a', 'abbr', 'acronym', 'b', 'cide', 'em', 'i', 'strong']
target.body_html = bleach.linkify(bleach.clean(
markdown(value, output_format='html'),
tags = allowed_tags, strip=True))
db.event.listen(Comment.body, 'set', Comment.on_changed_body)
Comment模型的屬性幾乎和Post模型一樣,不過多了一個disabled字段,這是個布爾值字段,協管員通過這個字段查禁不當評論,和博客文章一樣,評論也定義了一個事件,在修改body字段內容時觸發,自動把Markdown文本轉換成HTML,轉換過程和之前的博客文章一樣,不過評論相對較短,而且對Markdown中允許使用的HTML標籤要求更嚴格,要刪除與段落相關的標籤,只留下格式化字符的標籤
爲了完成對數據庫的修改,User和Post模型還要建立與comments表的一對多關係,如下:
# app/models.py
class User(db.model):
#...
comments = db.relationship('Comment', backref='author', lazy='dynamic')
class Post(db.Model):
#...
comments = db.relationship('Comment', backref='post', lazy='dynamic')
提交和顯示評論
在這個程序中,評論要顯示在單篇博客文章頁面中,這個頁面在之前添加固定鏈接時已經創建,在這個頁面中還要有一個提交評論的表單,用來輸入評論的簡單表單如下:
# app/main/forms.py
class CommentForm(Form):
body = StringFIeld('', validators=[Required()])
submit = SubmitField('Submit')
下例是爲了支持評論而更新的/post/<int:id>
路由
# app/main/views.py
# ...
@main.route('/post/<int:id>')
def post(id):
post = Post.query.get_or_404(id)
form = CommentForm()
if form.validate_on_submit():
comment = Comment(body=form.body.data,
post=post,
author=current_user._get_curernt_object())
db.session.add(comment)
flash('Your comment has been published.')
return redirect(url_for('.post', id=post.id, page=-1))
page = request.args.get('page', 1, type=int)
if page == -1:
page = (post.comments.count() - 1) / \
current_app.config['FLASKY_COMMENTS_PER_PAGE'] + 1
pagination = post.comments.order_by(Comment.timestamp.asc()).paginate(
page, per_page=current_app.config['FLASKY_COMMENTS_PER_PAGE'],
error_out=False)
comments = pagination.items
return render_template('post.html', posts=[post], form=form,
comments=comments, pagination=pagination)
這個視圖函數實例化了一個評論表單,並將其轉入post.html
模板,以便渲染,提交表單後,插入新評論的邏輯和處理博客文章的過程差不多,和Post模型一樣,評論的author
字段也不能直接設爲current_user
,因爲這個變量是上下文代理對象,真正的User對象要使用字段表達式current_user._get_current_object()
獲取
評論按照時間戳順序排列,新評論顯示在列表的底部,提交評論後,請求結果是一個重定向,轉回之前的URL,但是在url_for()
函數的參數中把page
設爲-1
,這是個特殊的頁數,用來請求評論的最後一頁,所以剛提交的評論纔會出現在頁面中,程序從查詢字符串中獲取頁數,發現值爲-1
時,會計算評論的總量和總頁數,得出真正要實現的頁數
文章的評論列表通過post.comments
一對多關係獲取,按照時間戳順序進行排列,再使用博客文章相同的技術分頁顯示,評論列表對象和分頁對象都傳入了模板,以便渲染,FLASKY_COMMENTS_PER_PAGE
配置變量也被加入config.py
中,用來控制每頁顯示的評論數量
評論的渲染過程在新模板_comments.html
中進行,類似於_posts.html
,但使用的CSS類不同,_comments.html
模板要引入post.html
中,放在文章正文下方,後面再顯示分頁導航,新模板內容如下:
<ul class="comments">
{% for comment in comments %}
<li class="comment">
<div class="comment-thumbnail">
<a href="{{ url_for('.user', username=comment.author.username) }}">
<img class="img-rounded profile-thumbnail" src="{{ comment.author.gravatar(size=40) }}">
</a>
</div>
<div class="comment-content">
<div class="comment-date">{{ moment(comment.timestamp).fromNow() }}</div>
<div class="comment-author"><a href="{{ url_for('.user', username=comment.author.username) }}">{{ comment.author.username }}</a></div>
<div class="comment-body">
{% if comment.body_html %}
{{ comment.body_html | safe }}
{% else %}
{{ comment.body }}
{% endif %}
</div>
</div>
</li>
{% endfor %}
</ul>
爲了完善功能,我們還要在首頁和資料頁中加上指向評論頁面的鏈接,如下:
# app/templates/_posts.html
# ...
<a href="{{ url_for('.post', id=post.id) }}#comments">
<span class="label label-primary">
{{ post.comments.count() }} Comments
</span></a>
注意鏈接文本中顯示評論數量的方法,評論數量可以使用SQLAlchemy提供的count()
過濾器輕易的從posts和comments表的一對多關係中獲取
指向評論頁的鏈接結構也值得一說,這個鏈接的地址是在文章的固定鏈接後面加上一個#comments
後綴,這個後綴稱爲URL片段,用於指定加載頁面後滾動條所在的初始位置,Web瀏覽器會尋找id等於URL片段的元素並滾動頁面,讓這個元素顯示在窗口頂部,這個初始位置被設爲post.html
模板中評論區的標題,即<h4 id="comments">Comments<h4>
除此之外,分頁導航所用的宏也要做某些改動,評論的分頁導航鏈接也要加上#comments
片段,因此在post.html
模板中調用宏時,傳入片段參數