1.知识预览
在本节中将会学习到以下的内容
-
sqlachemy的分页查询方法
-
flask-ckeditor富文本编辑器的使用
-
bootstrap 分页渲染
2.数据库模型
作为一个论坛的应用程序,那么帖子是必不可少的东西。因此在实现首页功能之前,需要先在数据库中建立对应表,来存储这些数据。打开`bbs/models.py模块,嵌入Post模型类
class Post(db.Model):
__tablename__ = 't_post'
id = db.Column(db.INTEGER, primary_key=True, autoincrement=True, index=True)
title = db.Column(db.String(100), index=True, nullable=False)
content = db.Column(db.TEXT, nullable=False)
textplain = db.Column(db.TEXT, nullable=False)
create_time = db.Column(db.DateTime, default=datetime.datetime.now)
update_time = db.Column(db.DateTime, default=datetime.datetime.now)
is_anonymous = db.Column(db.INTEGER, default=1, comment='post is anonymous? 2: yes 1: no')
read_times = db.Column(db.INTEGER, default=0)
# 用户对帖子的操作
likes = db.Column(db.INTEGER, default=0, comment='like post persons')
unlikes = db.Column(db.INTEGER, default=0, comment='unlike post persons')
collects = db.Column(db.INTEGER, default=0, comment='collect post persons')
# 外键id
cate_id = db.Column(db.INTEGER, db.ForeignKey('t_postcate.id'))
author_id = db.Column(db.INTEGER, db.ForeignKey('t_user.id'))
status_id = db.Column(db.INTEGER, db.ForeignKey('t_status.id'), default=1)
# 用户关系
cats = db.relationship('PostCategory', back_populates='post')
user = db.relationship('User', back_populates='post')
status = db.relationship('Status', back_populates='post')
在上面的代码中,定义了Post
类的相关属性。论坛里面的帖子数量庞大,且种类也十分多,因此使用了外键cate_id
来关联帖子的类别,同时使用author_id
来关联当前帖子属于哪个用户发布。用户发布了帖子之后当然也可以删除帖子,使用status_id
来关联帖子的状态。同时当帖子被举报次数较多时候,论坛管理员也可以屏蔽帖子,因此通过status_id
可以来定义帖子的不同状态。
在前面的章节中,已经建好了user、status表的相关信息,因此这里只需要创建Category模型类即可,同时需要在status模型中将post的外键关系加进去,继续在该模块中加入下面的代码。
class PostCategory(db.Model):
__tablename__ = 't_postcate'
id = db.Column(db.INTEGER, primary_key=True, autoincrement=True)
name = db.Column(db.String(40), nullable=False)
create_time = db.Column(db.Date, default=datetime.date.today)
post = db.relationship('Post', back_populates='cats', cascade='all')
class Status(db.Model):
# 省略已有的代码
post = db.relationship('Post', back_populates='status', cascade='all')
到此为止,目前关于帖子相关的数据库表的信息就创建完成了。但是在帖子类别表中还没有数据,我们可以手动到数据库中去添加,或者在bbs/__init__.py
文件中添加下面的代码.
def register_cmd(app: Flask):
@app.cli.command()
def init():
init_cate()
click.echo('初始化帖子类别表完成!')
def init_cate():
categories = ['杂谈', '趣事', '表白', '寻物', '咸鱼', '活动']
for category in categories:
pc = PostCategory(name=category)
db.session.add(pc)
db.session.commit()
在init_cate()函数中定义了六种帖子类别,这里读者可以自由发挥。然后我们在终端中输入下面的命令将帖子类别数据加入到数据库中
flask init
使用该命令会清楚数据库中的原始数据!
3.flask-migrate
我们创建完了数据库之后,可以根据第三节的flask init
来初始化数据库,但是如果我们在开发环境中有很多测试数据了,如果进行数据库初始化,那将会把原来的数据都清空掉。如果我们使用的是MySQL数据库,我们可以通过flask-migrate
这个工具来对数据库进行升级以及迁移。
首先在虚拟环境中安装flask-migrate
pip install flask-migrate
安装程序执行完成之后,需要在我们的程序中实例化migrate
。打开bbs/extensions.py
模型,添加下面的代码
from flask_migrate import Migrate
mg = Migrate()
然后打开bbs/__init__.py
模块,将实例化的migrate初始化
from extensions import mg
def register_extensions(app: Flask):
# 省略已有代码
mg.init_app(app, db)
完成上面的操作之后,在终端中输入下面的命令,创建数据库迁移环境,
flask db init
我们可以在我们项目的同级目录中看到一个migrations
的文件夹,该文件中保存的就是数据迁移的相关文件,使用下面的命令创建迁移文件。
flask db migrate -m "add post table"
在上面的命令中-m是可选的参数,加上只是为了可以track
某些操作,当我们迁移出错之后。然后打开migrations
文件夹,我们可以看到生成的迁移文件,文件内容读者可以自行去查看,然后通过下面的命令进行数据库的升级操作。
flask db upgrade
在进行git上传的时候,最好是吧migrations文件夹加入.gitignore文件中去,这样我们在生产环境进行数据库迁移的时候就不会出现冲突了。
4.发布帖子
在创建好相应的前序工作之后,我们就可以开始实现发布帖子的功能了。在实现发布帖子的功能,我们使用到了flask-ckeditor库,通过该库可以很轻松的实现富文本编辑功能,首先需要安装flask-ckeditor。
a.flask-ckedito配置
pip install flask-ckeditor
安装完成之后,使用CKEditor需要做一些初始化配置,打开bbs/settting.py模块,在BaseConfig类中添加下面的代码
class BaseConfig(object):
# 省略已有代码
# CKEditor configure
CKEDITOR_SERVE_LOCAL = True
CKEDITOR_ENABLE_CODESNIPPET = True
CKEDITOR_HEIGHT = 400
CKEDITOR_FILE_UPLOADER = 'normal.image_upload'
在给CKEditor
配置文件上传路由时,配置了normal.image_upload
路由,但是此时我们的应用程序中还没有这个路由,因此需要创建这个路由,打开bbs/blueprint/frontend/
文件夹,创建一个名为normal.py
的模块,添加下面的代码
from flask import Blueprint
normal_bp = Blueprint('normal', __name__, url_prefix='/normal')
@normal_bp.route('/image/upload/')
def image_upload():
pass
之后需要把该蓝图在__init__.py
文件中进行注册,在这里我们先不对文件上传视图函数做处理,后面我们再来处理这个函数。
b.创建表单
同样使用wtfform来渲染前端创建帖子的表单,打开bbs/forms.py
模块,添加下面的代码
class BasePostForm(FlaskForm):
title = StringField(u'标题', validators=[DataRequired(message='帖子标题不能为空'),
Length(min=1, max=50, message='用户名长度必须在1到50位之间')],
render_kw={'class': '', 'rows': 50, 'placeholder': '输入您的帖子标题'})
category = SelectField(label=u'分区',
default=0,
coerce=int)
anonymous = SelectField(label=u'是否匿名', default=1, choices=[(1, '实名'), (2, '匿名')], coerce=int)
body = CKEditorField('帖子内容', validators=[DataRequired(message='请输入帖子内容')])
submit = SubmitField(u'发布', render_kw={'class': 'source-button btn btn-primary btn-xs mt-2 text-right'})
def __init__(self, *args, **kwargs):
super(BasePostForm, self).__init__(*args, **kwargs)
categories = PostCategory.query.all()
self.category.choices = [(cate.id, cate.name) for cate in categories]
# noinspection PyMethodMayBeStatic
class CreatePostForm(BasePostForm):
def validate_title(self, filed):
if Post.query.filter_by(title=filed.data).first():
raise ValidationError('该标题已存在请换一个!')
class EditPostForm(BasePostForm):
submit = SubmitField(u'保存编辑', render_kw={'class': 'source-button btn btn-danger btn-xs mt-2 text-right'})
跟用户登录、注册表单类似,因为帖子除了发布之外还可以编辑,因此可以通过基类定义共同属性,在子类中定义各自的特有属性。在基类中我们定义了表单的字段,同时通过构造函数去获取了PostCategory
中所有类别,并将其值赋给category
字段了。
c.后端逻辑
在bbs/blueprint/frontend/
中创建名为post.py
的模块,添加下面的代码
import datetime
from flask import Blueprint, render_template, flash, redirect, url_for, request, jsonify, current_app
from bbs.blueprint.frontend.normal import to_html
from bbs.models import Post, Collect, PostReport, ReportCate, Comments, Notification, CommentStatistic, PostStatistic, \
PostCategory
from bbs.forms import CreatePostForm, EditPostForm
from flask_login import login_required, current_user
from bbs.extensions import db
from bbs.utils import get_text_plain, EMOJI_INFOS
from bbs.decorators import statistic_traffic
post_bp = Blueprint('post', __name__, url_prefix='/post')
@post_bp.route('/new/', methods=['GET', 'POST'])
@login_required
def new_post():
form = CreatePostForm()
if form.validate_on_submit():
title = form.title.data
cate = form.category.data
anonymous = form.anonymous.data
content = form.body.data
textplain = get_text_plain(content)
post = Post(title=title, cate_id=cate, content=content, is_anonymous=anonymous, author_id=current_user.id,
textplain=textplain)
db.session.add(post)
db.session.commit()
flash('帖子发布成功!', 'success')
return redirect(url_for('post.read', post_id=post.id))
return render_template('frontend/post/new-post.html', form=form)
在该模块中,前端用户所有关于帖子的操作都会存放于此,因此在实例化蓝图的时候添加了url_prefix
位置参数,此蓝图中的所有视图函数都会有post
的前缀url,在该蓝图中的所有视图函数都是用于帖子的相关操作。
与此同时,我们添加了一个new_post()视图函数,在视图函数上面添加了两个装饰器
-
post_bp.rout()
路由装饰器,这里就不做过多的解释了,属于flask的最基本知识点
-
login_required
这个装饰器是用来判断限定只有用户登录之后才能进行发布帖子的操作,属于
flask_login
第三方模块的装饰器。其原理就是通过session来判断用户是否登录,如果没有则跳转到系统定义的登录页面;
之后实例化了上一步创建的表单类,通过validate_form_submit
来判断用户是提交表单数据还是初始进入创建帖子页面,如果是初始进入页面则返回模板文件渲染,如果是提交则将用户提交的信息添加到数据库中,实现发布帖子的功能。
现在我们还没有创建对应的模板文件,接下来就需要创建发布帖子的模板文件。
d.发布帖子模板文件
打开bbs/templates/frontend/post/
文件夹,新建new-post.html
文件,在文件中添加如下代码
{% extends "frontend/base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}
发布新的帖子
{% endblock %}
{% block content %}
<body>
<main>
<div class="container mt-3">
<h3 class="text-info"><strong>新的帖子</strong></h3>
<hr class="bg-secondary">
<form action="/post/new/" method="post">
{{ form.csrf_token }}
{{ wtf.form_field(form.title) }}
<div class="row">
<div class="col">
{{ wtf.form_field(form.category) }}
</div>
<div class="col">
{{ wtf.form_field(form.anonymous) }}
</div>
</div>
{{ form.body }}
<div class="text-right">
{{ form.submit }}
</div>
</form>
</div>
</main>
</body>
{{ ckeditor.load() }}
{{ ckeditor.config(name='body') }}
{% endblock %}
同样的还是通过继承基模板来实现我们的页眉跟页脚的布局,然后在content块中定义我们自己的内容。content中的内容跟注册表单差不多,就是把后端的wtfform字段进行渲染。
{{ ckeditor.load() }}
{{ ckeditor.config(name='body') }}
通过这两段代码我们就渲染了CKEditor的富文本编辑器了。尤其注意config中的name='body',这个body就是我们BasePostForm中定义的字段名,根据该字段名就会将CKEditor渲染到该前端元素中去。然后我们在浏览器中输入http://127.0.0.1:5000/post/new/就可以看到下面的页面了,CKEditor最上边工具栏中的图片上传还不能使用,在后面的章节中将会进行处理。
在页面中随便输入一些信息,点击发布,然后到数据库中就可以看到相关的帖子数据啦。
好啦,感谢各位看官读到这里,本节的功能就全部完成了。在下一节中将会实现阅读帖子内容,以及首页相关内容。
清水 博主 2021-01-26T11:49:34
Mikoro shinobi