创建博客-评论部分(提交和显示)

评论在数据库中的表示

评论和博客文章没有太大区别,都有正文、作者和时间戳,而且在这个特定实现中都使用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模板中调用宏时,传入片段参数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值