展开

文章目录

修改历史

历史修改版本

  1. 2021-01-26 19:40:55
  2. 2021-01-26 11:43:27

使用Flask搭建一个校园论坛5-帖子发布

2021-01-26 11:43:15 系列教程 225

简介

在上一节中,实现了用户注册、登录的功能。作为一个论坛最基本的属性,已经完成了,这节将实现发布帖子的功能。

1.知识预览

在本节中将会学习到以下的内容

  1. sqlachemy的分页查询方法

  2. flask-ckeditor富文本编辑器的使用

  3. 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()视图函数,在视图函数上面添加了两个装饰器

  1. post_bp.rout()

    路由装饰器,这里就不做过多的解释了,属于flask的最基本知识点

  2. 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最上边工具栏中的图片上传还不能使用,在后面的章节中将会进行处理。

发布帖子

在页面中随便输入一些信息,点击发布,然后到数据库中就可以看到相关的帖子数据啦。

好啦,感谢各位看官读到这里,本节的功能就全部完成了。在下一节中将会实现阅读帖子内容,以及首页相关内容。

当前共有2条评论


清水 博主

Mikoro shinobi


清水 博主

Bsokuki