发布帖子后台逻辑完成
首先给帖子设计个模型,编辑apps.models.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class PostModel(db.Model): __tablename__ = 'post' id = db.Column(db.Integer, primary_key=True, autoincrement=True) title = db.Column(db.String(200), nullable=False) content = db.Column(db.Text, nullable=False) create_time = db.Column(db.DateTime, default=datetime.now) board_id = db.Column(db.Integer, db.ForeignKey('board.id')) author_id = db.Column(db.String(100), db.ForeignKey('front_user.id'), nullable=False) board = db.relationship("BoardModel", backref="posts") author = db.relationship("FrontUser", backref='posts')
同步到数据库
python manage.py db migrate
python manage.py db upgrade
发表帖子是需要前台用户登录才可以的,因此,我先完成下验证前台用户是否登录的装饰器,创建front.decorators.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
from functools import wraps from flask import session, redirect, url_for import config def login_required(func): @wraps(func) def inner(*args, **kwargs): if config.FRONT_USER_ID in session: return func(*args, **kwargs) else: return redirect(url_for('front.signin')) return inner
写一个钩子函数,用来全局使用登录用户的信息,创建front.hooks.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
from .views import bp import config from flask import session,g,render_template from .models import FrontUser @bp.before_request def my_before_request(): if config.FRONT_USER_ID in session: user_id = session.get(config.FRONT_USER_ID) user = FrontUser.query.get(user_id) if user: g.front_user = user
编辑front.__init__.py
from .views import bp from . import hooks
编辑front.views.py,发布帖子的视图函数,首先写个form验证
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
class AddPostForm(BaseForm): title = StringField(validators=[InputRequired(message='请输入标题!')]) content = StringField(validators=[InputRequired(message='请输入内容!')]) board_id = IntegerField(validators=[InputRequired(message='请输入板块id!')])
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
@bp.route('/apost/', methods=['GET', 'POST']) @login_required def apost(): if request.method == 'GET': boards = BoardModel.query.all() return render_template('front/front_apost.html', boards=boards) else: add_post_form = AddPostForm(request.form) if add_post_form.validate(): title = add_post_form.title.data content = add_post_form.content.data board_id = add_post_form.board_id.data board = BoardModel.query.get(board_id) if not board: return xjson.json_param_error(message='没有这个板块') post = PostModel(title=title, content=content) post.board = board post.author = g.front_user db.session.add(post) db.session.commit() return xjson.json_success() else: return xjson.json_param_error(message=add_post_form.get_error())
配置UEditor富文本编辑器
进入 下载页面 下载软件包
解压软件包,把php目录里面的config.json复制出来,然后删除php目录
在flask项目static目录下创建目录ueditor,把以上图中的目录文件拷贝到static/ueditor中
编辑flask配置文件config.py,添加如下配置
# UEditor的相关配置 #上传到本地 #UEDITOR_UPLOAD_PATH = os.path.join(os.path.dirname(__file__),'images') #上传到七牛 UEDITOR_UPLOAD_TO_QINIU = True #如果上传到七牛这里设置为True,上传到本地则为False UEDITOR_QINIU_ACCESS_KEY = "xxxxx" UEDITOR_QINIU_SECRET_KEY = "xxxxx" UEDITOR_QINIU_BUCKET_NAME = "xxxx" UEDITOR_QINIU_DOMAIN = "http://xxxx"
flask需要一个视图路由来处理,这里我们配置一个蓝图
在apps下新建一个python packge命名为ueditor, 在uedittor下新建ueditor.py
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
from flask import ( Blueprint, request, jsonify, url_for, send_from_directory, current_app as app ) import json import re import string import time import hashlib import random import base64 import sys import os from urllib import parse # 更改工作目录。这么做的目的是七牛qiniu的sdk # 在设置缓存路径的时候默认会设置到C:/Windows/System32下面 # 会造成没有权限创建。 os.chdir(os.path.abspath(sys.path[0])) try: import qiniu except: pass from io import BytesIO bp = Blueprint('ueditor',__name__,url_prefix='/ueditor') UEDITOR_UPLOAD_PATH = "" UEDITOR_UPLOAD_TO_QINIU = False UEDITOR_QINIU_ACCESS_KEY = "" UEDITOR_QINIU_SECRET_KEY = "" UEDITOR_QINIU_BUCKET_NAME = "" UEDITOR_QINIU_DOMAIN = "" @bp.before_app_first_request def before_first_request(): global UEDITOR_UPLOAD_PATH global UEDITOR_UPLOAD_TO_QINIU global UEDITOR_QINIU_ACCESS_KEY global UEDITOR_QINIU_SECRET_KEY global UEDITOR_QINIU_BUCKET_NAME global UEDITOR_QINIU_DOMAIN UEDITOR_UPLOAD_PATH = app.config.get('UEDITOR_UPLOAD_PATH') if UEDITOR_UPLOAD_PATH and not os.path.exists(UEDITOR_UPLOAD_PATH): os.mkdir(UEDITOR_UPLOAD_PATH) UEDITOR_UPLOAD_TO_QINIU = app.config.get("UEDITOR_UPLOAD_TO_QINIU") if UEDITOR_UPLOAD_TO_QINIU: try: UEDITOR_QINIU_ACCESS_KEY = app.config["UEDITOR_QINIU_ACCESS_KEY"] UEDITOR_QINIU_SECRET_KEY = app.config["UEDITOR_QINIU_SECRET_KEY"] UEDITOR_QINIU_BUCKET_NAME = app.config["UEDITOR_QINIU_BUCKET_NAME"] UEDITOR_QINIU_DOMAIN = app.config["UEDITOR_QINIU_DOMAIN"] except Exception as e: option = e.args[0] raise RuntimeError('请在app.config中配置%s!'%option) csrf = app.extensions.get('csrf') if csrf: csrf.exempt(upload) def _random_filename(rawfilename): letters = string.ascii_letters random_filename = str(time.time()) + "".join(random.sample(letters,5)) filename = hashlib.md5(random_filename.encode('utf-8')).hexdigest() subffix = os.path.splitext(rawfilename)[-1] return filename + subffix @bp.route('/upload/',methods=['GET','POST']) def upload(): action = request.args.get('action') result = {} if action == 'config': config_path = os.path.join(bp.static_folder or app.static_folder,'ueditor','config.json') with open(config_path,'r',encoding='utf-8') as fp: result = json.loads(re.sub(r'\/\*.*\*\/','',fp.read())) elif action in ['uploadimage','uploadvideo','uploadfile']: image = request.files.get("upfile") filename = image.filename save_filename = _random_filename(filename) result = { 'state': '', 'url': '', 'title': '', 'original': '' } if UEDITOR_UPLOAD_TO_QINIU: if not sys.modules.get('qiniu'): raise RuntimeError('没有导入qiniu模块!') buffer = BytesIO() image.save(buffer) buffer.seek(0) q = qiniu.Auth(UEDITOR_QINIU_ACCESS_KEY, UEDITOR_QINIU_SECRET_KEY) token = q.upload_token(UEDITOR_QINIU_BUCKET_NAME) ret,info = qiniu.put_data(token,save_filename,buffer.read()) if info.ok: result['state'] = "SUCCESS" result['url'] = parse.urljoin(UEDITOR_QINIU_DOMAIN,ret['key']) result['title'] = ret['key'] result['original'] = ret['key'] else: image.save(os.path.join(UEDITOR_UPLOAD_PATH, save_filename)) result['state'] = "SUCCESS" result['url'] = url_for('ueditor.files',filename=save_filename) result['title'] = save_filename, result['original'] = image.filename elif action == 'uploadscrawl': base64data = request.form.get("upfile") img = base64.b64decode(base64data) filename = _random_filename('xx.png') filepath = os.path.join(UEDITOR_UPLOAD_PATH,filename) with open(filepath,'wb') as fp: fp.write(img) result = { "state": "SUCCESS", "url": url_for('files',filename=filename), "title": filename, "original": filename } return jsonify(result) @bp.route('/files/<filename>/') def files(filename): return send_from_directory(UEDITOR_UPLOAD_PATH,filename)
编辑ueditor.__init__.py
from .ueditor import bp
在主程序中注册蓝图
... from apps.ueditor import bp as ueditor_bp app.register_blueprint(ueditor_bp)
前台配置
在templates/front下创建front_apost.html
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{% extends "front/front_base.html" %} {% block title %} 发布帖子 {% endblock %} {% block head %} <script src="{{ url_for('static',filename='ueditor/ueditor.config.js') }}"></script> <script src="{{ url_for('static',filename='ueditor/ueditor.all.min.js') }}"></script> {% endblock %} {% block body %} <form action="" method="post"> <div class="form-group"> <div class="input-group"> <span class="input-group-addon">标题</span> <input type="text" class="form-control" name="title"> </div> </div> <div class="form-group"> <div class="input-group"> <span class="input-group-addon">板块</span> <select name="board_id" class="form-control"> {% for board in boards %} <option value="{{ board.id }}">{{ board.name }}</option> {% endfor %} </select> </div> </div> <div class="form-group"> <script id="editor" type="text/plain" style="height:500px;"></script> </div> <div class="form-group"> <button class="btn btn-danger" id="submit-btn">发布帖子</button> </div> </form> {% endblock %}
在static/front/js下创建front_apost.js
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/** * Created by Administrator on 2018/10/6. */ /** * Created by hynev on 2017/12/31. */ $(function () { var ue = UE.getEditor("editor",{ "serverUrl": '/ueditor/upload/' }); $("#submit-btn").click(function (event) { event.preventDefault(); var titleInput = $('input[name="title"]'); var boardSelect = $("select[name='board_id']"); var title = titleInput.val(); var board_id = boardSelect.val(); var content = ue.getContent(); bbsajax.post({ 'url': '/apost/', 'data': { 'title': title, 'content':content, 'board_id': board_id }, 'success': function (data) { if(data['code'] == 200){ xtalert.alertConfirm({ 'msg': '恭喜!帖子发表成功!', 'cancelText': '回到首页', 'confirmText': '再发一篇', 'cancelCallback': function () { window.location = '/'; }, 'confirmCallback': function () { titleInput.val(""); ue.setContent(""); } }); }else{ xtalert.alertInfo(data['message']); } } }); }); });
在front_apost.html中引入front_apost.js
{% block head %} ... <script src="{{ url_for('static',filename='front/js/front_apost.js') }}"></script> {% endblock %}
编辑front_index.html
<a href="{{ url_for("front.apost") }}" class="btn btn-warning btn-block">发布帖子</a>