评论在数据库中的表示
由于评论和2个模型有关系,分别是谁发了评论,以及评论了哪个文章,所以这次要更新数据库模型
models.py 创建用户评论数据库模型
class Comment(db.Model): __tablename__ = 'comments' id = db.Column(db.Integer, primary_key=True) body = db.Column(db.Text) timestamp = db.Column(db.DateTime,index=True,default=datetime.utcnow) body_html = db.Column(db.Text) 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)
User和Post里面也要和Comment设置相应的关系
class User(db.Model): # ... comments = db.relationship('Comment', backref='author', lazy='dynamic')
class Post(db.Model): # ... comments = db.relationship('Comment', backref='post', lazy='dynamic')
提交和显示评论
main/forms.py 创建评论输入表单
class CommentForm(FlaskForm): body = StringField('输入你的评论',validators=[DataRequired()]) submit = SubmitField('提交')
为支持评论更新路由post
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(): # current_user(上下文代理对象)._get_current_object() 真正的User对象 comment = Comment(body=form.body.data, post=post, author=current_user._get_current_object()) db.session.add(comment) flash('你已经发布了新评论.') # page设为-1,用来请求评论的最后一页,刚提交的评论才会出现在页面中 return redirect(url_for('.post', id=post.id, page=-1)) page = request.args.get('page', 1, type=int) # 程序获取页数,发现时-1时,会计算总量和总页数得出真正显示的页数 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)
评论模板 _comment.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>
评论引入post.html
{% extends "base.html" %} {% import "bootstrap/wtf.html" as wtf %} {% import '_macros.html' as macros %} {% block title %}Flasky - 文章{% endblock %} {% block page_content %} {% include '_posts.html' %} <h4 id="comments">评论</h4> {% if current_user.can(Permission.COMMENT) %} <div class="comment-form"> {{ wtf.quick_form(form) }} </div> {% endif %} {% include '_comments.html' %} {% if pagination %} <div class="pagination"> {{ macros.pagination_widget(pagination, '.post', fragment='#comments', id=posts[0].id) }} </div> {% endif %} {% endblock %}
上面加在 _posts.html里面的这段话,在url地址后面,加了一个 #comments
这个叫做URL片段
可以理解为:#这个符号,在URL地址里面相当于分隔符
#左边的,代表真实URL,对外的,就是普通用户可以看到的URL
#右边的,代表对网页内部的代码,在浏览器的地址栏是看不到的,他针对的是,当前页面HTML文件内,id=comments的内容
具体作用是:当加载这个页面时,直接会将这个id处于的位置,滚到页面顶端
类似于平时的用户体验是:当评论完以后,页面重新加载,评论区域会自动翻滚到页面最上端,体验很好。
按了comments按钮以后,可以直接跳转到post页面的评论区域,等于是个页中页功能。
链接到博客的评论_post.html
<a href="{{ url_for('.post', id=post.id) }}#comments"> <span class="label label-primary">{{ post.comments.count() }} 评论</span> </a>
user.html 增加评论数
<p><b> 评论数量:</b>{{ user.comments.count() }}.</p>
_macro.html 宏需要加入片断参数
{% macro pagination_widget(pagination,endpoint,fragment='') %} <ul class="pagination"> <li {% if not pagination.has_prev %}class="disabled" {% endif %}> <a href="{% if pagination.has_prev %}{{ url_for(endpoint,page = pagination.prev_num,**kwargs) }}{{ fragment }}{% else %}#{% endif %}"> « </a> </li> {% for p in pagination.iter_pages() %} {% if p %} {% if p ==pagination.page %} <li class="active"> <a href="{{ url_for(endpoint,page=p,**kwargs) }}{{ fragment }}">{{ p }}</a> </li> {% else %} <li> <a href="{{ url_for(endpoint,page=p,**kwargs) }}{{ fragment }}">{{ p }}</a> </li> {% endif %} {% else %} <li class="disabled"><a href="#">…</a></li> {% endif %} {% endfor %} <li {% if not pagination.has_next %}class="disabled" {% endif %}> <a href="{% if pagination.has_next %}{{ url_for(endpoint,page = pagination.next_num,**kwargs) }}{{ fragment }}{% else %}#{% endif %}"> » </a> </li> </ul> {% endmacro %}
管理评论
base.html加入评论管理导航条
{% if current_user.can(Permission.MODERATE_COMMENTS) %} <li> <a href="{{ url_for('main.moderate') }}">评论管理</a> </li>
{% endif %}
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)
moderate.html 评论管理页面模板
{% extends "base.html" %} {% import '_macros.html' as macros %} {% block title %}Flasky - 评论管理{% endblock %} {% block page_content %} <div class="page-header"> <h1>评论管理</h1> </div> {% set moderate = True %} # 决定是否渲染评论管理功能 {% include '_comments.html' %} {% if pagination %} <div class="pagination"> {% if pagination %} <div class="pagination"> {{ macros.pagination_widget(pagination, '.moderate') }} </div> {% endif %} </div> {% endif %} {% endblock %}
_comments.html 评论正文
<div class="comment-body"> {% if comment.disabled %} <p><i>此评论已被管理员禁用</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) }}">恢复</a> {% else %} <a class="btn btn-default btn-xs" href="{{ url_for('.moderate_disable',id=comment.id,page=page) }}">禁用</a> {% endif %} {% endif %}
</div>
main.views.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)))