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 支持页面与类文件的热部署 ...
随机推荐
- linux shell读取配置文件
配置文件CoverageInfo FTP_URL=ftp://svn-fb.sicent.com:21/jenkins/Jifei_Repo/OL-2/IDC_Platform/bar_seats_c ...
- RDLC - 后台代码直接导出Excel/PDF/Word格式
最近做报表功能,用到了.net的报表组件rdlc. 其中有个功能就是后台代码直接输出Excel/PDF/Word格式的文件,网上看了些资源,做个总结: 参考地址 我直接贴出代码: //自动导出exce ...
- 【BZOJ4837】LRU算法 [模拟]
LRU算法 Time Limit: 6 Sec Memory Limit: 128 MB[Submit][Status][Discuss] Description 小Q同学在学习操作系统中内存管理的 ...
- Katu Puzzle(POJ3678+2-SAT问题+tarjan缩点)
题目链接:http://poj.org/problem?id=3678 题目: 题意:给你a,b,c,op,op为逻辑运算符或.与.异或,使得a op b = c,让你判断这些运算符是否存在矛盾,不存 ...
- js 基本类型&引用类型
1.基本的数据类型有:undefined,boolean,number,string,null.基本类型的访问是按值访问的,就是说你可以操作保存在变量中的实际的值 基本类型的比较是值的比较.用==比较 ...
- Angular2.0 基础:双向数据绑定 [(ngModel)]
在属性绑定中,值从模型到屏幕上的目标属性 (property). 通过把属性名括在方括号中来标记出目标属性,[]. 这是从模型到视图的单向数据绑定. 而在事件绑定中,值是从屏幕上的目标属性 到 mod ...
- 将资源文件夹中的文件通过流的方式写入到应用的File文件夹中
//1.在Files文件夹中创建同名的数据库文件 File files = getFilesDir(); File file = new File(files, DBName); if(file.ex ...
- C基础 如何得到文件长度
引言 有一天看见看到返回文件长度代码返回值都是long,就感觉怪怪的, 一般32位long最大也就2G. 而大文件太多了, 一个Dota2安装包估计都得10多G吧. 一般C得到文件长度代码 /* * ...
- OC 07 类的扩展
1.NSDate的使用 NSDate是Cocoa中⽤于处理⽇期和时间的基础类,封装了某⼀给定的时刻(含日期,时间,时区) 注意NSLog(@“%@”,nowDate);⽆论你是哪个时区的时间,打印时总 ...
- 根据日期查询年龄js
function ages(str) { var r = str.match(/^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/); if(r==null)return f ...