Flask实战第58天:发布帖子功能完成
发布帖子后台逻辑完成
首先给帖子设计个模型,编辑apps.models.py
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')
apps.models.py
同步到数据库
python manage.py db migrate
python manage.py db upgrade
发表帖子是需要前台用户登录才可以的,因此,我先完成下验证前台用户是否登录的装饰器,创建front.decorators.py
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.decotators.py
写一个钩子函数,用来全局使用登录用户的信息,创建front.hooks.py
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.hooks.py
编辑front.__init__.py
from .views import bp
from . import hooks
编辑front.views.py,发布帖子的视图函数,首先写个form验证
class AddPostForm(BaseForm):
title = StringField(validators=[InputRequired(message='请输入标题!')])
content = StringField(validators=[InputRequired(message='请输入内容!')])
board_id = IntegerField(validators=[InputRequired(message='请输入板块id!')])
front.forms.py
@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())
front.views.py
配置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
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.py
编辑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
{% 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 %}
front_apost.html
在static/front/js下创建front_apost.js
/**
* 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.js
在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>
Flask实战第58天:发布帖子功能完成的更多相关文章
- Flask实战第64天:帖子加精和取消加精功能完成
帖子加精和取消加精是在cms后台来设置的 后台逻辑 首页个帖子加精设计个模型表,编辑apps.models.py class HighlightPostModel(db.Model): __table ...
- Flask实战第65天:帖子按照发布时间和评论数量等排序
排序,我们需要在前端传递参数, 编辑front_index.html 编辑front.views.py from apps.models import HighlightPostModel from ...
- Flask实战第62天:帖子详情页布局
在templates/front/下创建详情页面front_pdetail.html 编辑front.views.py创建详情页的视图函数 from flask import abort ... @b ...
- Flask实战第61天:帖子板块过滤显示
先在显示的帖子是所有版块的帖子,这节我们来完成点击某个版块,则显示此版块的帖子 要完成这个功能,我们需要在前端传递板块的id到后台, 编辑front_index.html 编辑首页视图 编辑板块选中样 ...
- Flask实战第60天:帖子分页技术实现
编辑manage.py,添加测试帖子 @manager.command def create_test_post(): for x in range(1, 100): title = '标题{}'.f ...
- 一百三十八:CMS系统之发布帖子前端js
先补一个功能,根据扥状态显示用户名/退出或者登录/注册 from .views import bpimport configfrom flask import session, gfrom .mode ...
- SpringBoot开发十五-发布帖子
需求介绍 使用 AJAX 异步通信实现网页能够增量的更新呈现到页面上而不需要刷新整个页面. 现在基本上都是服务器返回 JSON 字符串来解析 代码实现 使用 JQuery 发送 AJAX 请求. 首先 ...
- Flask实战-留言板-安装虚拟环境、使用包组织代码
Flask实战 留言板 创建项目目录messageboard,从GreyLi的代码中把Pipfile和Pipfile.lock文件拷贝过来,这两个文件中定义了虚拟环境中需要安装的包的信息和位置,进入m ...
- Spring Boot 揭秘与实战(八) 发布与部署 - 开发热部署
文章目录 1. spring-boot-devtools 实现热部署 2. Spring Loaded 实现热部署 3. 模板文件热部署 4. 源代码 Spring Boot 支持页面与类文件的热部署 ...
随机推荐
- Spark的Shuffle过程介绍
Spark的Shuffle过程介绍 Shuffle Writer Spark丰富了任务类型,有些任务之间数据流转不需要通过Shuffle,但是有些任务之间还是需要通过Shuffle来传递数据,比如wi ...
- 【NOIP】提高组2016 愤怒的小鸟
[题意]Universal Online Judge [算法]状态压缩型DP [题解]看数据范围大概能猜到是状压了. 根据三点确定一条抛物线,枚举两个点之间的抛物线,再枚举有多少点在抛物线上(压缩为状 ...
- 【POJ】2892 Tunnel Warfare
[算法]平衡树(treap) [题解]treap知识见数据结构 在POJ把语言从G++换成C++就过了……??? #include<cstdio> #include<algorith ...
- floyd骚操作——传递闭包
传递闭包的含义指通过传递性推导出尽量多的元素之间的关系,而传递闭包一般都是采用floyd算法. 下面用两道题来实现传递闭包: Problem 1(POJ3660): 题目链接:http://poj.o ...
- HDU 1070 Milk (模拟)
题目链接 Problem Description Ignatius drinks milk everyday, now he is in the supermarket and he wants to ...
- bzoj 1014 splay
首先我们可以用splay来维护这个字符串,那么对于某两个位置的lcp,维护每个节点的子树的hash,然后二分判断就好了. /************************************** ...
- javascript中break和continue
1.break break语句会立即退出循环,强制执行循环后面的语句 var num = 0; for(var i=1;i<10;i++){ if(i%5 == 0){ break; } num ...
- 【转】png文件格式
前言 我们都知道,在进行J2ME的手机应用程序开发的时候,在图片的使用上,我们可以使用PNG格式的图片(甚至于在有的手机上,我们只可以使用PNG 格式的图片),尽管使用图片可以为我们的应用程序增加不少 ...
- webpack版本1与版本2的若干写法区别
2.x的环境遇到类似this._init is not a function的报错. 版本1.x的写法: resolve: { extensions: ['', '.js', '.vue'] }, m ...
- 【bzoj4373】算术天才⑨与等差数列
同之前那道由乃题,可以认为由乃题是这题的特殊情况…… 维护方法是同样的,维护区间和,区间平方和即可. 注意特判一个数(其实没有必要) #include<bits/stdc++.h> ; u ...