第十三章 用户评论(一)

本节实现用户评论功能

一. 评论在数据库中的表示

评论包含正文、作者和时间戳;用户和评论,文章和评论均为一对多的关系,我们需要在多这一次侧定义外键:

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, default=datetime.utcnow, index=True)
    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'))

disabled字段:协管员通过这个字段查禁不当评论。与博客文章一样,我们也需要定义一个事件,在修改body字段内容时触发,自动把Markdown文本转换为HTML:

@db.event.listens_for(Comment.body, 'set', named=True)
def on_change_body(**kwargs):
    allowed_tags = ['a', 'abbr', 'acronym', 'b', 'code', 'em', 'i', 'strong']
    kwargs['target'].body_html = bleach.linkify(bleach.clean(markdown(kwargs['value'], output_format='html'),tags=allowed_tags, strip=True))

注意:

  • 装饰器中将named设为True,被装饰函数才可以接受关键字传参;
  • MarkDown中允许使用的HTML标签要求更严格,要删除与段落相关的标签,只留下格式化字符的标签;

User模型与Comment模型做为1这一侧,还需要定义关系(通常我们在多一侧定义外键后,选择在一这一侧定义关系):

class User(UserMixin, db.Model):
    __tablename__ = "users"
    ...
    comments = db.relationship('Comment', backref='author', lazy='dynamic')

class Post(db.Model):
    __tablename__ = 'posts'
    ...
    comments = db.relationship('Comment', backref='post', lazy='dynamic')

二. 提交和显示评论

 app/main/forms.py 评论输入表单:

class CommentForm(FlaskForm):
    body = StringField('', validators=[DataRequired()])
    submit = SubmitField('Submit')

为了支持表单提交,/post/<int:id>路由要做些修改:

@main.route('/post/<int:id>', methods=['GET', 'POST'])
def post(id):
    post = Post.query.get_or_404(id)
    form = CommentForm()
    if current_user.can(Permission.COMMENT) and form.validate_on_submit():
        comment = Comment(body=form.body.data, post=post, author=current_user._get_current_object())
        db.session.add(comment)
        db.session.commit()
        flash('Your comment has been published.')
        return redirect(url_for('.post', id=post.id, page=-1))
    page = request.args.get('page', 1, type=int)
    per_page = current_app.config['FLASKY_COMMENTS_PER_PAGE']
    if page == -1:
        # 计算总页数
        page = (post.comments.count() - 1) // per_page + 1
    pagination = post.comments.order_by(Comment.timestamp.asc()).paginate(page, per_page, error_out=False)
    comments = pagination.items
    return render_template('post.html', posts=[post], form=form, comments=comments, pagination=pagination)

注意:

  • 实例化Comment时使用的author字段不能直接设为current_user,因为这个变量是上下文代理对象。真正的User对象要使用表达式current_user._get_current_object()获取;
  • 评论按时间戳正序排列,默认显示第一页(即最早的评论);当用户发表新评论后,重定向到当前页面时,将page设为-1,显示最后一页。此时我们需要计算总评论量和总页数,以便得出真正要显示的页数;

最后,我们在首页和资料页分别添加指向评论页面的链接:

<div class="post-footer">
    ....
    <a href="{{ url_for('.post', id=post.id) }}#comments">
        <span class="label label-primary">{{ post.comments.count() }} Comments</span>
    </a>
</div>

注意:指向评论页的链接结构中包含“#comments”后缀,这个后缀称为URL片段,用于指定加载页面后滚动条所在的初始位置。此处滚动条的初始位置被设为"Comments"标题,其中HTML代码为<h4 id="#comments">Comments</h4>;

在用户资料页面:

发布了132 篇原创文章 · 获赞 14 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Geroge_lmx/article/details/104767078