1. 评论在数据库中的表示
app/models.py: Comment 模型
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‘, ‘code‘, ‘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)
app/models/user.py: users表和posts表与comments表之间的一对多关系
class User(db.Model): # ... comments = db.relationship(‘Comment‘, backref=‘author‘, lazy=‘dynamic‘) class Post(db.Model): # ... comments = db.relationship(‘Comment‘, backref=‘post‘, lazy=‘dynamic‘)
2. 提交和娴熟评论
app/main/forms.py: 评论输入表单
class CommentForm(Form): body = StringField(‘‘, validators=[Required()]) submit = SubmitField(‘Submit‘)
app/main/views.py: 支持评论的文章路由
@main.route(‘/post/<int:id>‘, methods=[‘GET‘, ‘POST‘]) 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_current_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)
评论按照时间戳顺序排列,新评论显示在列表的底部。提交评论后,请求结果是一个重定向,转回之前的 URL,但是在 url_for() 函数的参数中把 page 设为 -1,这是个特殊的页数,用来请求评论的最后一页,所以刚提交的评论才会出现在页面中。程序从查询字符串中获取页数,发现值为 -1 时,会计算评论的总量和总页数,得出真正要显示的页数。
app/templates/_posts.html:添加链接到博客文章的评论
<a href="{{ url_for(‘.post‘, id=post.id) }}#comments"> <span class="label label-primary"> {{ post.comments.count() }} Comments </span> </a>
#comments 后缀。这个后缀称为 URL 片段,用于指定加载页面后滚动条所在的初始位置。Web 浏览器会寻找 id 等于 URL 片段的元素并滚动页面,让这个元素显示在窗口顶部。这个初始位置被设为 post.html 模板中评论区的标题,即 <h4 id="comments">Comments<h4>。
3. 管理评论
app/templates/base.html:在导航条中加入管理评论链接
... {% if current_user.can(Permission.MODERATE_COMMENTS) %} <li><a href="{{ url_for(‘main.moderate‘) }}">Moderate Comments</a></li> {% endif %} ...
app/main/views.py:管理评论的路由
@main.route(‘/moderate‘) @login_required @permission_required(Permission.MODERATE_COMMENTS) def moderate(): page = request.args.get(‘page‘, 1, type=int) pagination = Comment.query.order_by(Comment.timestamp.desc()).paginate( page, per_page=current_app.config[‘FLASKY_COMMENTS_PER_PAGE‘], error_out=False) comments = pagination.items return render_template(‘moderate.html‘, comments=comments, pagination=pagination, page=page)
app/templates/moderate.html:评论管理页面的模板
{% extends "base.html" %} {% import "_macros.html" as macros %} {% block title %}Flasky - Comment Moderation{% endblock %} {% block page_content %} <div class="page-header"> <h1>Comment Moderation</h1> </div> {% set moderate = True %} {% include ‘_comments.html‘ %} {% if pagination %} <div class="pagination"> {{ macros.pagination_widget(pagination, ‘.moderate‘) }} </div> {% endif %} {% endblock %}
这个模板将渲染评论的工作交给 _comments.html 模板完成,但把控制权交给从属模板之前,会使用 Jinja2 提供的 set 指令定义一个模板变量 moderate,并将其值设为 True。这个变量用在 _comments.html 模板中,决定是否渲染评论管理功能。
app/templates/_comments.html:渲染评论的正文
... <div class="comment-body"> {% if comment.disabled %} <p></p><i>This comment has been disabled by a moderator.</i></p> {% endif %} {% if moderate or not comment.disabled %} {% if comment.body_html %} {{ comment.body_html | safe }} {% else %} {{ comment.body }} {% endif %} {% endif %} </div> {% if moderate %} <br> {% if comment.disabled %} <a class="btn btn-default btn-xs" href="{{ url_for(‘.moderate_enable‘, id=comment.id, page=page) }}">Enable</a> {% else %} <a class="btn btn-danger btn-xs" href="{{ url_for(‘.moderate_disable‘,id=comment.id, page=page) }}">Disable</a> {% endif %} {% endif %} ...
_comments.html 模板中的按钮指定了 page 参数,重定向后会返回之前的页面。
app/main/vies.py: 评论管理路由
@main.route(‘/moderate/enable/<int:id>‘) @login_required @permission_required(Permission.MODERATE_COMMENTS) def moderate_enable(id): comment = Comment.query.get_or_404(id) comment.disabled = False db.session.add(comment) return redirect(url_for(‘.moderate‘, page=request.args.get(‘page‘, 1, type=int))) @main.route(‘/moderate/disable/<int:id>‘) @login_required @permission_required(Permission.MODERATE_COMMENTS) def moderate_disable(id): comment = Comment.query.get_or_404(id) comment.disabled = True db.session.add(comment) return redirect(url_for(‘.moderate‘, page=request.args.get(‘page‘, 1, type=int)))
2015-05-27