个人博客Demo: link.
GitHub项目完整链接:link
回顾上一节主要讲了以下2个方面内容:
- 博客文章管理,包括新建文章,编辑文章,删除文章,文章管理界面,图片上传设置
- 博客设置
4.2.1 评论管理
-
关闭/开启评论功能和删除评论功能已经在介绍文章详情页时介绍过,不再赘述
-
评论审核查看和评论筛选
a. 对于新的评论,在登录时的导航栏也会有new提示和未读数量提示在评论表格操作页添加一个已读按钮,如果评论的reviewed字段为False,则显示“已读”按钮,并将该行评论以橙色背景显示
b. 我们将评论分为三类:所有评论,未读评论和管理发布的评论。使用查询参数filter传入筛选的评论类型,这三个类型分别用all,unread和admin表示
c. 因为这个"已读"操作也会修改数据因此使用form表单元素提交POST请求,manage_comment.html代码如下:
{% extends 'base.html' %}
{% from 'bootstrap/pagination.html' import render_pagination %}
{% block title %}管理评论{% endblock %}
{% block content %}
<div class="page-header">
<h1>评论数:
<small class="text-muted">{{ pagination.total }}</small>
</h1>
<!-- 设置评论筛选过滤导航栏 -->
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link disabled" href="#">Filter </a>
</li>
<li class="nav-item">
<!-- request.args.get('filter', 'all')获取查询参数filter -->
<a class="nav-link {% if request.args.get('filter', 'all') == 'all' %}active{% endif %}"
href="{{ url_for('admin.manage_comments', filter='all') }}">全部</a>
</li>
<li class="nav-item">
<a class="nav-link {% if request.args.get('filter') == 'unread' %}active{% endif %}"
href="{{ url_for('admin.manage_comments', filter='unread') }}">未读 {% if unread_comments %}
<!-- 设置未读评论徽章 -->
<span class="badge badge-success">{{ unread_comments }}</span>{% endif %}</a>
</li>
<li class="nav-item">
<a class="nav-link {% if request.args.get('filter') == 'admin' %}active{% endif %}"
href="{{ url_for('admin.manage_comments', filter='admin') }}">管理员</a>
</li>
</ul>
</div>
{% if comments %}
<table class="table table-striped">
<thead>
<tr>
<th>No.</th>
<th>作者</th>
<th>来自文章</th>
<th>内容</th>
<th>日期</th>
<th>操作状态</th>
</tr>
</thead>
{% for comment in comments %}
<!-- 如果comment未审核,则table表格颜色为warning黄色 -->
<tr {% if not comment.reviewed %}class="table-warning" {% endif %}>
<td>{{ loop.index + ((pagination.page - 1) * config['BLOG_MANAGE_COMMENT_PER_PAGE']) }}</td>
<td>
<!-- 显示评论作者名及来源url -->
{% if comment.from_admin %}{{ admin.name }}{% else %}{{ comment.author }}{% endif %}<br>
<!-- 管理员评论徽章 -->
{% if comment.from_admin %}
<span class="badge badge-primary">Author</span>
{% endif %}
</td>
<td>{{ comment.post.title }}</td>
<td>{{ comment.body }}</td>
<td>{{ moment(comment.timestamp).format('LL') }}</td>
<td>
<!-- 未审核添加approve审核标签 -->
{% if not comment.reviewed %}
<form class="inline" method="post"
action="{{ url_for('.approve_comments', comment_id=comment.id, next=request.full_path) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-success btn-sm">已读</button>
</form>
{% endif %}
<form class="inline" method="post"
action="{{ url_for('.delete_comments', comment_id=comment.id, next=request.full_path) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-danger btn-sm"
onclick="return confirm('Are you sure?');">删除
</button>
</form>
</td>
</tr>
{% endfor %}
</table>
<div class="page-footer">{{ render_pagination(pagination) }}</div>
{% else %}
<div class="tip"><h5>这里一条评论也没有...</h5></div>
{% endif %}
{% endblock %}
管理评论页面视图函数 manage_comments代码如下:
@admin_bm.route('/comment/manages')
@login_required
def manage_comments():
filter_rule = request.args.get('filter', 'all') # 从查询字符串获取过滤规则
page = request.args.get('page', 1, type=int)
if filter_rule == 'unread':
filtered_comments = Comment.query.filter_by(reviewed=False)
elif filter_rule == 'admin':
filtered_comments = Comment.query.filter_by(from_admin=True)
else:
filtered_comments = Comment.query
# 筛选后的实例化对象进行排序
pagination = filtered_comments.order_by(Comment.timestamp.desc()).paginate(
page, per_page=current_app.config['BLOG_MANAGE_COMMENT_PER_PAGE'])
comments = pagination.items
return render_template('admin/manage_comments.html', pagination=pagination, comments=comments)
- 视图函数中通过获取查询参数"filter"(默认值为all),返回筛选过后的(filter_by)Comment对象,然后分页对象在筛选过后的评论基础之上进行处理,最后渲染模板传递到manage_comments.html中
审核查看评论approve_comments视图函数代码:
@admin_bm.route('/comment/<int:comment_id>/approve', methods=['POST'])
@login_required
def approve_comments(comment_id):
comment = Comment.query.get_or_404(comment_id)
comment.reviewed = True
db.session.commit()
flash('评论已读', 'success')
return redirect_back()
4.2.2 分类管理
新增分类
- 新增分类 new_category,html 代码:
{% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_form %}
{% block title %}新建分类{% endblock %}
{% block content %}
<div class="page-header">
<h1>新建分类</h1>
</div>
<div>
{{ render_form(form) }}
</div>
{% endblock content %}
- 新增分类 new_category视图函数代码:
新增导入模块
from Blog.forms import CategoryForm
admin_bm.route('/category/new', methods=['GET', 'POST'])
@login_required
def new_category():
form = CategoryForm()
if form.validate_on_submit():
name = form.name.data
category = Category(
name=name
)
db.session.add(category)
db.session.commit()
flash("新建分类成功!", 'success')
return redirect(url_for('.manage_category'))
return render_template('admin/new_category.html', form=form)
分类管理页面
- 分类管理页面 manage_categroy.html代码:
{% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_form %}
{% block title %}分类管理{% endblock title %}
{% block content %}
<div class="page-header">
<h1 ><small class="text-muted">分类共:{{ categories|length }} 个</small>
<span class="float-right">
<a class="btn btn-primary btn-sm" href="{{ url_for('.new_category')}}">新建</a>
</span>
</h1>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>No.</th>
<th>分类名</th>
<th>文章数</th>
<th>操作</th>
</tr>
</thead>
{% for category in categories %}
<tr>
<td>{{ loop.index }}</td>
<td><a href="{{ url_for('blog.show_category', category_id=category.id)}}">{{ category.name }}</a></td>
<td>{{ category.posts|length }}</td>
<!-- 除默认第一个默认分类外都添加Edit和Delete按钮,设置到删除总是采用POST方法,防范CSRF攻击 -->
<td>
{% if category.id != 1 %}
<a class="btn btn-info btn-sm" href="{{ url_for('.edit_category', category_id=category.id)}}">编辑</a>
<form class="inline" method="post" action="{{ url_for('.delete_category', category_id=category.id)}}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('确定删除此分类?');">
删除
</button>
</form>
</td>
{% endif %}
</tr>
{% endfor %}
</table>
<p class="text-muted">Tips: 删除分类不会删除分类下的文章,相关文章将会移动到默认分类之中。
</p>
{% endblock content %}
- 分类管理页面manage_category视图函数代码:
@admin_bm.route('/category/manage')
@login_required
def manage_category():
return render_template('admin/manage_category.html')
编辑分类
- 编辑分类edit_category.html代码
{% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_form %}
{% block title %}编辑分类{% endblock title %}
{% block content %}
<div class="page-header">
<h1>编辑分类</h1>
</div>
{{ render_form(form) }}
{% endblock content%}
- 编辑分类edit_category 视图函数代码: 其中默认分类不能更改,设置flash提醒
@admin_bm.route('/category/<int:category_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_category(category_id):
form = CategoryForm()
category = Category.query.get_or_404(category_id)
# 默认分类不能更改
if category_id == 1:
flash('你不能修改默认分类', 'warning')
return redirect(url_for('blog.index'))
if form.validate_on_submit():
category.name = form.name.data
db.session.commit()
flash('分类更新成功!', 'success')
return redirect(url_for('.manage_category'))
form.name.data = category.name
return render_template('admin/edit_category.html', form=form)
删除分类
- 删除分类,其中默认分类不能删除,删除分类只删除分类名,分类下所有文章移动到默认分类中。
- 在Blog/models.py数据库模型文件中的Category类中定义delete方法,用于删除分类而将分类下文章移动到默认分类中
- Category模型类中添加delete()方法:
def delete(self):
default_category = Category.query.get(1) # 获取默认分类
posts = self.posts[:] # 将删除分类下所有文章复制到posts变量中
for post in posts:
post.category = default_category # 将post分类参数设置为default
db.session.delete(self) # 删除原分类
db.session.commit()
- 删除分类delete_category 视图函数代码:
@admin_bm.route('category/<int:category_id>/delete', methods=['POST'])
@login_required
def delete_category(category_id):
category = Category.query.get_or_404(category_id)
if category.id == 1:
flash('不能删除默认分类!', 'warning')
return redirect(url_for('blog.index'))
# 特殊的删除分类方法,在Category类中定义
category.delete()
flash('删除分类成功!', 'success')
return redirect(url_for('.manage_category'))
到此为止我们的博客已经编写完毕,下一节将简单介绍如何将博客部署到pythonanywhere。