創建博客-評論部分(提交和顯示)

評論在數據庫中的表示

評論和博客文章沒有太大區別,都有正文、作者和時間戳,而且在這個特定實現中都使用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模板中調用宏時,傳入片段參數

發佈了34 篇原創文章 · 獲贊 23 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章