本章将以博客为例实践前几章学习的内容,讲解部分实例
项目框架
首先看下ufrom的使用:
app.py下关联CSRFProtect
csrf = CSRFProtect(app=app)
在apps下的user下创建一个form.py文件定义登录表单输入格式
import re
from flask import session
from flask_wtf import FlaskForm, RecaptchaField
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms import StringField,PasswordField
from wtforms.validators import DataRequired, Length, ValidationError, EqualTo
class UserForm(FlaskForm):
name = StringField(label = 'name',validators=[DataRequired(),Length(min=6,max=12,message='用户名长度必须在6-12之间')])
password = PasswordField(label = 'password',validators=[DataRequired(),Length(min=6,max=12,message='密码长度必须在6-12之间')])
confirm_password = PasswordField(label = 'repassword',validators=[DataRequired(),Length(min=6,max=12,message='密码长度必须在6-12之间'),EqualTo('password','两次密码不一致')])
phone = StringField(label="手机号码",validators=[DataRequired(),Length(min=11,max=11,message="手机长度必须为11位")])
icon = FileField(label="用户头像",validators=[FileRequired(),FileAllowed(['jpg','png','gift'],message="必须是图片文件格式")])
recaptcha = RecaptchaField(label="验证码")
recaptchabyself = StringField(label='验证码')
def validate_recaptchabyself(self,data):
input_code = data.data
code = session.get('valid')
if input_code.lower() != code.lower():
raise ValidationError("验证码错误!")
# 自定义方法,必须要以validate_开头
def validate_name(self,data):
print('--------->',type(self.name))
print('========>',type(data))
if self.name.data[0].isdigit():
raise ValidationError('用户名不能以数字开头')
def validate_phone(self,data):
phone = data.data
if not re.search(r'^1[35678]\d{9}$',phone):
raise ValidationError("手机格式错误!")
然后再user下的views中需要表单验证的方法下实例化UserForm()表单
@users_bp.route('/testForm',methods=['GET','POST'])
def testForm():
uform = UserForm()
if uform.validate_on_submit():
print(uform.name)
print(uform.password)
name = uform.name.data
password = uform.password.data
phone = uform.phone.data
icon = uform.icon.data
filename = secure_filename(icon.filename)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_DIR = os.path.join(BASE_DIR,'static')
UPLOAD_DIR = os.path.join(STATIC_DIR,'upload')
icon.save(os.path.join(UPLOAD_DIR,filename))
return '提交成功!'
return render_template('formtest.html',uform)
# from与bootstrap结合使用
@users_bp.route('/user',methods=['GET','POST'])
def boot_form_user():
uform = UserForm()
return render_template('from_bootstrap.html',uform=uform)
最后在html中使用
formtest.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<style>
p span{
font-size: 14px;
color: red;
}
style>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js">script>
head>
<body>
<form action="" method="post" enctype="multipart/form-data">
{{ uform.csrf_token }}
<p>{{ uform.name.label }}:{{ uform.name }}<span>{% if uform.name.errors %}{{ uform.name.errors.0 }}{% endif %}span>p>
<p>{{ uform.password.label }}:{{ uform.password }}<span>{% if uform.password.errors %}{{ uform.password.errors.0 }}{% endif %}span>p>
<p>{{ uform.confirm_password.label }}:{{ uform.confirm_password }}<span>{% if uform.confirm_password.errors %}{{ uform.confirm_password.errors.0 }}{% endif %}span>p>
<p>{{ uform.phone.label }}:{{ uform.phone }}<span>{% if uform.phone.errors %}{{ uform.phone.errors.0 }}{% endif %}span>p>
<p>{{ uform.icon.label }}:{{ uform.icon }}<span>{% if uform.icon.errors %}{{ uform.icon.errors.0 }}{% endif %}span>p>
<p>{{ uform.recaptcha.label }}:{{ uform.recaptcha }}<span>{% if uform.recaptcha.errors %}{{ uform.recaptcha.errors.0 }}{% endif %}span>p>
<p>{{ uform.recaptchabyself.label }}:{{ uform.recaptchabyself }}<img src="{{ url_for('users.get_image') }}" alt="" id="img">
<p>{% if uform.recaptcha.errors %}{{ uform.recaptcha.errors.0 }}{% endif %}p>
<p><input type="submit" value="提交"> p>
form>
<script>
$('#img').vlivk(function(){
$(this).attr('src',"{{ url_for('get_image') }}?ran="+Math.random());
})
script>
body>
html>
form_bootstrap.html
{% extends 'bootstrap/base.html'%}
{% import 'bootstrap/wtf.html' as wtf %}
{% block styles %}
{{ super() }}
{% endblock %}
{% block content %}
<form action="{{ url_for('hello_word') }}" method="post" enctype="multipart/form-data">
{{ uform.csrf_token }}
<p>{{ wtf.quick_form( uform, buttom_map = {'submit_button':'primary'}, horizontal_columns=('lg',5,2) ) }}p>
<p>{{ wtf.form_field(uform.name) }}p>
form>
{% endblock %}
接下来进入正文,这里我们会提到两个模型:article、user,将围绕这两个模块构建一个简单的博客系统,包含增删改查操作,以及一些复杂关系关联等。
user的模型对象:
from datetime import datetime
from exts import db
class User(db.Model):
id = db.Column(db.Integer,primary_key = True,autoincrement = True)
username = db.Column(db.String(15),unique=True,nullable=False)
password = db.Column(db.String(256),nullable=False)
phone = db.Column(db.String(11),nullable=False,unique= True)
email = db.Column(db.String(30))
icon = db.Column(db.String(100))
isdelete = db.Column(db.Boolean,default=False)
rdatetime = db.Column(db.DateTime,default = datetime.now())
articles = db.relationship('Article',backref='user')
comments = db.relationship('Comment',backref='user')
def __str__(self):
return self.username
class Photo(db.Model):
id = db.Column(db.Integer,primary_key = True,autoincrement = True)
photo_name = db.Column(db.String(50),nullable=False)
photo_datatime = db.Column(db.DateTime,default=datetime.now())
user_id = db.Column(db.Integer,db.ForeignKey('user.id'))
def __str__(self):
return self.photo_name
class AboutMe(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
content = db.Column(db.BLOB,nullable=False)
pdatetime = db.Column(db.DateTime, default=datetime.now())
user_id = db.Column(db.Integer, db.ForeignKey('user.id'),unique=True)
user = db.relationship('User', backref='about')
def __str__(self):
return self.content
class MessageBoard(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
content = db.Column(db.String(256), nullable=False)
mdatetime = db.Column(db.DateTime, default=datetime.now())
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship('User', backref='messages')
article的模型对象:
from datetime import datetime
from exts import db
class Article_type(db.Model):
id = db.Column(db.Integer,primary_key=True, autoincrement=True)
type_name = db.Column(db.String(20),nullable=False)
articles = db.relationship('Article',backref='articletype')
class Article(db.Model):
id = db.Column(db.Integer,primary_key=True, autoincrement=True)
title = db.Column(db.String(50),nullable=False)
content = db.Column(db.Text,nullable=False)
pdatetime = db.Column(db.DateTime,default=datetime.now())
click_num = db.Column(db.Integer,default=0)
save_num = db.Column(db.Integer,default=0)
love_num = db.Column(db.Integer,default=0)
# 外键 同步到数据库的外键关系
user_id = db.Column(db.Integer,db.ForeignKey('user.id'),nullable=False)
# 可在user/models.py中添加 articles = db.relationship('Article',backref='user')
# user = db.relationship('User',backref='articles')
type_id = db.Column(db.Integer,db.ForeignKey('article_type.id'))
comments = db.relationship('Comment',backref='article')
class Comment(db.Model):
# 自定义表的名字
# __tablename__ = 'comment'
id = db.Column(db.Integer,primary_key=True,autoincrement=True)
comment = db.Column(db.String(255),nullable=False)
user_id = db.Column(db.Integer,db.ForeignKey('user.id'))
article_id = db.Column(db.Integer,db.ForeignKey('article.id'))
cdatetime = db.Column(db.DateTime,default = datetime.now())
def __str__(self):
return self.comment
user的视图控制器views
import hashlib
import logging
import os
import time
from io import BytesIO
from flask import Blueprint, render_template, request, url_for, jsonify, session, g, make_response, flash, app
from flask_wtf import csrf
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import redirect, secure_filename
from apps.article.models import Article_type, Article
from apps.user.form import UserForm
from apps.user.models import User, Photo, AboutMe, MessageBoard
from apps.user.smssend import SmsSendAPIDemo
from apps.utils.util import upload_qiniu, delete_qiniu, user_type, sendMessage, generate_image
from exts import db, cache
from settings import Config
users_bp = Blueprint('users',__name__,url_prefix='/users')
# 访问以下url需要已登录状态
required_login_list = ['/users/center',
'/users/change',
'/users/publish',
'/users/upload_photo',
'/users/photo_del',
'/articles/add_comment',
'/users/aboutme',
'/users/showabout']
@users_bp.before_app_first_request
def first_request():
print('before_app_first_request')
if request.path in required_login_list:
print("需要验证用户的登录情况!")
@users_bp.before_app_request
def before_request1():
print('before_app_request',request.path)
if request.path in required_login_list:
id = session.get('uid')
print('------>',id)
if not id:
uform = UserForm()
return render_template('user/login.html',uform=uform)
else:
user = User.query.get(id)
# g对象 本次请求的对象
g.user = user
print(g.user.username)
@users_bp.after_app_request
def after_request_test(response):
print('after_app_request')
response.set_cookie('a','bbbb',max_age=19)
return response
@users_bp.teardown_app_request
def teardown_request_test(response):
print('teardown_app_request')
# 自定义过滤器
@users_bp.app_template_filter('cdecode')
def content_decode(content):
content = content.decode('utf-8')
return content[:200]
# 自定义过滤器
@users_bp.app_template_filter('cdecode1')
def content_decode1(content):
content = content.decode('utf-8')
return content
# 首页
@users_bp.route('/')
@cache.cached(timeout=50)
def index():
# 1、cookie的获取
# uid = request.cookies.get('uid',None)
# # 2、session的获取,session默认
# uid = session.get('uid')
user,types = user_type()
page = int(request.args.get('page',1))
print(page)
# 判断是否存在检索词汇
search = request.args.get('search','')
# 有检索词汇
if search:
# 进行检索contains
pagination = Article.query.filter(Article.title.contains(search)).order_by(-Article.pdatetime).paginate(page, per_page=1)
else:
# 获取文章列表
pagination = Article.query.order_by(-Article.pdatetime).paginate(page,per_page=1)
print(pagination.items)
print(pagination.page) # 当前页码数
print(pagination.prev_num) # 当前的前一个页码数
print(pagination.next_num) # 当前的后一页的页码数
print(pagination.has_next)
print(pagination.has_prev)
print(pagination.pages) # 总共有几页
print(pagination.total) # 总的记录条数
params = {
'user': user,
'types': types,
'pagination': pagination,
'search': search
}
# 模拟延时
for i in range(10):
time.sleep(0.5)
return render_template('user/index.html',**params)
# 用户注册
@users_bp.route('/register',methods=['POST','GET'])
def register():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
repassword = request.form.get('repassword')
phone = request.form.get('phone')
email = request.form.get('email')
if password == repassword:
user = User()
user.username = username
# user.password = hashlib.md5(password.encode('utf-8')).hexdigest()
# 使用自带的函数实现加密:generate_password_hash
user.password = generate_password_hash(password)
user.phone = phone
user.email = email
db.session.add(user)
db.session.commit()
return redirect(url_for('users.index'))
return render_template('user/register.html',msg='用户确认密码不正确!')
return render_template('user/register.html')
# 手机号码验证
@users_bp.route('/checkphone',methods=['POST','GET'])
def check_phone():
phone = request.args.get('phone')
user = User.query.filter(User.phone == phone).all()
if user:
return jsonify(code=400,msg='此号码已被注册')
else:
return jsonify(code=200,msg='此号码可用')
# 登录
@users_bp.route('/login',methods=['POST','GET'])
def login():
print("我看你进来了没有")
if request.method == 'POST':
f = request.args.get('f')
# 手机密码登录
if f == '1':
username = request.form.get('username')
password = request.form.get('password')
users = User.query.filter(User.username == username).all()
for user in users:
# check_password_hash(加密后的密码, 传入的密码) 如果Flag=True表示匹配
Flag = check_password_hash(user.password, password)
if Flag:
'''
# 1、cookie实现机制
response = redirect(url_for('users.index'))
response.set_cookie('uid',str(user.id),max_age=1800)
return response
'''
# 2、session实现机制,session当成字典使用
session['uid'] = user.id
return redirect(url_for('users.index'))
else:
return render_template('user/login.html',msg='用户名或密码错误!')
# 手机验证码登录
elif f == '2':
phone = request.form.get('phone')
code = request.form.get('code')
# session中获取验证码
# valid_code = session.get('phone')
# redis缓存中获取验证码
valid_code = cache.get(phone)
print(valid_code)
user = User.query.filter(User.phone == phone).first()
if user:
if code == valid_code:
session['uid'] = user.id
return redirect(url_for('users.index'))
else:
return render_template('user/login.html', msg='验证码有误!')
else:
return render_template('user/login.html',msg='手机号码未注册!')
else:
return render_template('user/login.html')
# 退出登录
@users_bp.route('/logout')
def logout():
'''
# 1、cookie方式
response = redirect(url_for('user.index'))
# 通过response对象的delete_cookie(key),key就是要删除的cookie的key
response.delete_cookie('uid')
return response
'''
# 2、session方式
# del session['uid']
session.clear()
return redirect(url_for('users.index'))
# 发送短信
@users_bp.route('/sendMsg')
def send_message():
phone = request.args.get('phone')
user = User.query.filter(User.phone==phone).first()
print(user.username)
if user:
ret,code = sendMessage(phone)
'''
# session中存储验证码
session['phone'] = '122417'
return jsonify(cood=200,msg='短信发送成功!')
'''
if ret is not None:
if ret["code"] == 200:
# taskId = ret["data"]["taskId"]
# print("taskId = %s" % taskId)
# session[phone] = '122417'
# cache.set(key,value,timeout=second)
# 将验证码存入redis,并设置有效时间为180s
cache.set(phone, code, timeout=180)
return jsonify(cood=200,msg='短信发送成功!')
else:
print ("ERROR: ret.code=%s,msg=%s" % (ret['code'], ret['msg']))
return jsonify(cood=200, msg='短信发送失败!')
else:
return render_template('user/login.html', msg='手机号未注册!')
# 用户中心
@users_bp.route('/center')
def center():
user, types = user_type()
photos = Photo.query.filter(Photo.user_id == g.user.id).all()
return render_template('user/center.html',user=g.user,types=types,photos=photos)
# 图片的扩展名
ALLOWWD_EXTENDIONS = ['jpg','png','gif','bmp']
# 修改信息
@users_bp.route('/change',methods=['POST','GET'])
def user_change():
if request.method == 'POST':
username = request.form.get('username')
phone = request.form.get('phone')
email = request.form.get('email')
# 图片必须使用request.files.get()获取
icon = request.files.get('icon')
# 查询一下手机号码
users = User.query.all()
for user in users:
if user.phone == phone:
return render_template('user/center.html',user=g.user,msg='此号码已被注册!')
else:
# 属性:filename 用户获取文件的名字
# 方法:save(保存路径)
icon_name = icon.filename
suffix = icon_name.rsplit('.')[-1]
if suffix in ALLOWWD_EXTENDIONS:
# 保存文件名是符合python的命名规则
icon_name = secure_filename(icon_name)
file_path = os.path.join(Config.UPLOAD_ICON_DIR, icon_name)
icon.save(file_path)
user = g.user
user.username = username
user.phone = phone
user.email = email
path = 'upload/icon/'
user.icon = os.path.join(path,icon_name)
print('------------->',user.icon)
db.session.commit()
return redirect(url_for('users.center'))
else:
return render_template('user/center.html', user=g.user, msg='图片扩展名必须为:jpg,png,gif,bmp')
return render_template('user/center.html',user=g.user)
# 上传照片
@users_bp.route('/upload_photo',methods=['GET','POST'])
def upload_photo():
# 获取上传的内容
photo = request.files.get('photo')
ret,info = upload_qiniu(photo)
if info.status_code == 200:
photo = Photo()
photo.photo_name = ret['key']
photo.user_id = g.user.id
db.session.add(photo)
db.session.commit()
return '上传成功!'
else:
return '上传失败!'
# 展示图片
@users_bp.route('myphoto')
def myphoto():
page = int(request.args.get('page',1))
# 分页
photos = Photo.query.paginate(page=1,per_page=3)
user, types = user_type()
return render_template('user/myphoto.html',photos=photos,user=user,types=types)
# 删除相册图片
@users_bp.route('/photo_del',methods=['GET','POST'])
def photo_del():
pid = request.args.get('pid')
photo = Photo.query.get(pid)
filename = photo.photo_name
info = delete_qiniu(filename)
if info.status_code == 200:
db.session.delete(photo)
db.session.commit()
return redirect('users.user_center')
else:
return render_template('500.html',err_msg='删除相册失败!')
# 关于用户介绍添加
@users_bp.route('/aboutme',methods=['GET','POST'])
def about_me():
content = request.form.get('about')
try:
aboutme = AboutMe()
aboutme.content = content.encode('utf-8')
aboutme.user_id = g.user.id
db.session.add(aboutme)
db.session.commit()
except Exception as err:
return redirect(url_for('users.user_center'))
else:
return render_template('user/aboutme.html',user=g.user)
@users_bp.route('/showabout')
def show_about():
user, types = user_type()
return render_template('user/aboutme.html',user=g.user,types=types)
# 展示错误页
@users_bp.route('/error')
def test_error():
referer = request.headers.get('Referer',None)
return render_template('500.html',referer=referer)
# 留言板
@users_bp.route('/board',methods=['GET','POST'])
def show_board():
user, types = user_type()
uid= user.id
# 查询所有的留言内容
page = int(request.args.get('page',1))
boardes = MessageBoard.query.filter(MessageBoard.user_id == uid).order_by(-MessageBoard.mdatetime).paginate(page=page,per_page=3)
if request.method == 'POST':
content = request.form.get('board')
msg_board = MessageBoard()
msg_board.content = content
if uid:
msg_board.uid = uid
db.session.add(msg_board)
db.session.commit()
return redirect(url_for('users.show_board'))
return render_template('user/board.html',user=g.user,boardes=boardes)
# 删除留言
@users_bp.route('/board_del')
def delete_board():
bid = request.agrs.get('bid')
if bid:
msgboard = MessageBoard.query.get(bid)
db.session.delete(msgboard)
db.session.commit()
return redirect(url_for('users.user_center'))
@users_bp.route('/testForm',methods=['GET','POST'])
def testForm():
uform = UserForm()
if uform.validate_on_submit():
print(uform.name)
print(uform.password)
name = uform.name.data
password = uform.password.data
phone = uform.phone.data
icon = uform.icon.data
filename = secure_filename(icon.filename)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
STATIC_DIR = os.path.join(BASE_DIR,'static')
UPLOAD_DIR = os.path.join(STATIC_DIR,'upload')
icon.save(os.path.join(UPLOAD_DIR,filename))
return '提交成功!'
return render_template('formtest.html',uform)
@users_bp.route('/image')
def get_image():
im,code = generate_image()
# 将image对象转成二进制
buffer = BytesIO()
im.save(buffer,"JPEG")
buf_bytes = buffer.getvalue()
# 保存到session中
session['valid'] = code
response = make_response(buf_bytes)
response.headers['Content-Type'] = 'image/jpg'
return response
# from与bootstrap结合使用
@users_bp.route('/user',methods=['GET','POST'])
def boot_form_user():
uform = UserForm()
return render_template('from_bootstrap.html',uform=uform)
# flash
@users_bp.route('/flush',methods=['GET','POST'])
def test_flush():
if request.method == 'POST':
username = request.form.get('username')
if username == 'admin':
flash('登录成功!','info')
flash(username,'warning')
flash('hahha', 'error')
return render_template('index.html')
else:
app.logger.debug('这是一个debug测试')
app.logger.warning('这是一个warning测试')
app.logger.error('这个是一个error测试')
return render_template('flushtest.html')
logger = logging.getLogger('app')
logger.setLevel(level=logging.WARNING)
handler = logging.FileHandler("log2.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s -%(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
@users_bp.route('/logger')
def logger_test():
logger.warning("首页的警告!!!!!!")
app.logger.warning('首页警告2!!!!!')
return render_template('')
article的视图控制器views
'''
Author: 梁雅馨
CreateTime:
descript:
'''
from flask import Blueprint, request, g, url_for, render_template, jsonify, session
from werkzeug.utils import redirect
from apps.article.models import Article, Article_type, Comment
from apps.user.models import User
from exts import db
articles_bp = Blueprint('articles',__name__,url_prefix='/articles')
# 访问以下url需要已登录状态
required_login_list = ['/articles/publish',
'/articles/love',
'/articles/save',
'/articles/add_comment']
@articles_bp.before_app_first_request
def first_request():
print('before_app_first_request')
if request.path in required_login_list:
print("需要验证用户的登录情况!")
@articles_bp.before_app_request
def before_request1():
print('before_app_request',request.path)
if request.path in required_login_list:
id = session.get('uid')
print('------>',id)
if not id:
return render_template('user/login.html')
else:
user = User.query.get(id)
# g对象 本次请求的对象
g.user = user
print(g.user.username)
# 自定义过滤器
@articles_bp.app_template_filter('cdecode')
def content_decode(content):
content = content.decode('utf-8')
return content
# 发布文章
@articles_bp.route('/publish',methods=['POST','GER'])
def publish_article():
if request.method == 'post':
title = request.form.get('title')
type_id = request.method.get('type')
content = request.form.get('content')
article = Article()
article.title = title
article.type_id = type_id
article.content = content
article.user_id = g.user.id
db.session.add(article)
db.session.commit()
return redirect(url_for('users.index'))
# 展示文章详情
@articles_bp.route('/detail')
def articles_detail():
# 通过id获取文章
id= request.args.get('aid')
print(id)
article = Article.query.get(id)
# 获取文章分类
# types = Article_type.query.all(0)
# 登录用户
user_id = session.get('uid')
user = None
if user_id:
user = User.query.get(user_id)
page = int(request.args.get('page',1))
comment = Comment.query.filter(Comment.article_id == id).order_by(-Comment.cdatetime).paginate(page=page,per_page=5)
return render_template('articles/detail.html',article=article,user=user,comment=comment)
# 收藏
@articles_bp.route('/love')
def articles_love():
article_id = request.args.get('aid')
tag = request.args.get('tag')
article = Article.query.get(article_id)
if tag == '1':
article.love_num -= 1
else:
article.love_num += 1
db.session.commit()
return jsonify(num = article.love_num)
# 点赞
@articles_bp.route('/save')
def articles_save():
article_id = request.args.get('aid')
tag = request.args.get('tag')
article = Article.query.get(article_id)
if tag == '1':
article.save_num -= 1
else:
article.save_num += 1
db.session.commit()
return jsonify(num=article.save_num)
# 发表文章评论
@articles_bp.route('/add_comment',methods=['POST','GER'])
def article_comment():
if request.method == 'POST':
comments = request.form.get('comment')
user_id = g.user.id
article_id = request.form.get('aid')
print(article_id)
comment =Comment()
comment.comment = comments
comment.article_id = article_id
comment.user_id = user_id
db.session.add(comment)
db.session.commit()
return redirect(url_for('articles.articles_detail')+"?aid="+article_id)
return redirect(url_for('user.index'))
# 文章分类检索
@articles_bp.route('/type_search')
def type_search():
uid = session.get('uid')
user = None
if uid:
user = User.query.get(uid)
# 文章分类获取
types = Article_type.query.all()
# 获取tid
tid = int(request.args.get('tid',1))
page = int(request.args.get('page',1))
type = Article_type.query.get(tid)
articles = Article.query.filter(Article.articletype == type).order_by(-Article.pdatetime).paginate(page=page,per_page=1)
return render_template('articles/article_type.html',user=user,types=types,type=type,articles=articles)
工具方法 smssend.py
import hashlib
from hashlib import md5
import json
import random
import time
import urllib
import urllib.request
class SmsSendAPIDemo(object):
API_URL = "https://sms.dun.163.com/v2/sendsms"
VERSION = "v2"
def __init__(self, secret_id, secret_key, business_id):
"""
Args:
secret_id (str) 产品密钥ID,产品标识
secret_key (str) 产品私有密钥,服务端生成签名信息使用
business_id (str) 业务ID,易盾根据产品业务特点分配
"""
self.secret_id = secret_id
self.secret_key = secret_key
self.business_id = business_id
def gen_signature(self, params=None):
"""生成签名信息
Args:
params (object) 请求参数
Returns:
参数签名md5值
"""
buff = ""
for k in sorted(params.keys()):
buff += str(k) + str(params[k])
buff += self.secret_key
return hashlib.md5(buff.encode("utf-8")).hexdigest()
def send(self, params):
"""请求易盾接口
Args:
params (object) 请求参数
Returns:
请求结果,json格式
"""
params["secretId"] = self.secret_id
params["businessId"] = self.business_id
params["version"] = self.VERSION
params["timestamp"] = int(time.time() * 1000)
params["nonce"] = int(random.random() * 100000000)
params["signature"] = self.gen_signature(params)
try:
params = urllib.parse.urlencode(params)
params = params.encode('utf-8')
request = urllib.request.Request(self.API_URL, params)
content = urllib.request.urlopen(request, timeout=5).read()
return json.loads(content)
# response = request.post(self.API_URL, data=params)
# return response.json()
except Exception as ex:
print("调用API接口失败:", str(ex))
if __name__ == "__main__":
"""示例代码入口"""
SECRET_ID = "**********" # 产品密钥ID,产品标识
SECRET_KEY = "*************" # 产品私有密钥,服务端生成签名信息使用,请严格保管,避免泄露
BUSINESS_ID = "**************" # 业务ID,易盾根据产品业务特点分配
api = SmsSendAPIDemo(SECRET_ID, SECRET_KEY, BUSINESS_ID)
params = {
"mobile": "15010185644",
"templateId": "10084",
"paramType": "json",
"params": "{'code':200','time':'20211224'}"
# 国际短信对应的国际编码(非国际短信接入请注释掉该行代码)
# "internationalCode": "对应的国家编码"
}
ret = api.send(params)
if ret is not None:
if ret["code"] == 200:
taskId = ret["data"]["taskId"]
print("taskId = %s" % taskId)
else:
print ("ERROR: ret.code=%s,msg=%s" % (ret['code'], ret['msg']))
utils下的util.py
# -*- coding: utf-8 -*-
# flake8: noqa
import os
from random import random
import requests
from PIL import ImageFilter
from PIL.Image import Image
from PIL.ImageDraw import ImageDraw
from PIL.ImageFont import ImageFont
from PIL._imaging import font
from flask import session
from qiniu import Auth, put_file, etag
from qiniu import BucketManager
from apps.article.models import Article_type
from apps.user.models import User
from apps.user.smssend import SmsSendAPIDemo
from settings import Config
access_key = 'KWkj4za9qp5KYy7VDzkLrxC6OBJvZvtuO-28pFoB'
secret_key = 'aiLWEJDBPUR8Be_hv-nlg-NhIh3kusH5ch-wjTbN'
# 要上传的空间
bucket_name = 'mublog2022'
def user_type():
user_id = session.get('uid')
user = None
if user_id:
user = User.query.get(user_id)
types = Article_type.query.all()
return user,types
def sendMessage(phone):
SECRET_ID = "33fc0b00c3505c3ed4c710f965e309fa" # 产品密钥ID,产品标识
SECRET_KEY = "f3d9dac4fa83272242411e2f1a6b1ae0" # 产品私有密钥,服务端生成签名信息使用,请严格保管,避免泄露
BUSINESS_ID = "6288c6bfb7764a24a6cc226b2ffb61a8" # 业务ID,易盾根据产品业务特点分配
api = SmsSendAPIDemo(SECRET_ID,SECRET_KEY,BUSINESS_ID)
# secret_pair = SecretPair(SECRET_ID,SECRET_KEY)
# api = SmsSendAPIDemo(BUSINESS_ID,secret_pair)
# 随机产生验证码
code = ""
for i in range(4):
ran = random.randint(0,9)
code += str(ran)
params = {
"mobile": phone,
"templateId": "10084",
"paramType": "json",
"params": {'code':code,'time':'20211224'}
# 国际短信对应的国际编码(非国际短信接入请注释掉该行代码)
# "internationalCode": "对应的国家编码"
}
ret = api.send(params)
return ret,code
# 上传本地文件
def upload_qiniu(filestorage):
# 初始化Auth状态
q = Auth(access_key, secret_key)
# 初始化BucketManager
bucket = BucketManager(q)
# 获取文件名
filename = filestorage.filename
# 重新拼接成一个不重复的文件名
ran = random.randint(1,1000)
suffix = filename.rsplit('.')[-1]
key = filename.rsplit('.')[0]+ '_' +str(ran) + '.' +suffix
#生成上传 Token,可以指定过期时间等
token = q.upload_token(bucket_name, key, 3600)
#要上传文件的本地路径
# localfile = os.path.join(Config.UPLOAD_ICON_DIR,phone_name)
ret, info = put_file(token, key, filestorage.read(), version='v2')
# print(info)
assert ret['key'] == key
assert ret['hash'] == etag(filestorage.read())
return ret,info
def delete_qiniu(key):
# 初始化Auth状态
q = Auth(access_key, secret_key)
# 初始化BucketManager
bucket = BucketManager(q)
# 删除bucket_name 中的文件 key
ret, info = bucket.delete(bucket_name, key)
print(info)
assert ret == {}
return info
# 获取上传文件
def GetImage(phone_name):
# 初始化Auth状态
q = Auth(access_key, secret_key)
# 初始化BucketManager
bucket = BucketManager(q)
#获取文件的状态信息
ret, info = bucket.stat(bucket_name, phone_name)
print(info)
assert 'hash' in ret
# 私有空间下载
def download(bucket_domain,phone_name):
# 初始化Auth状态
q = Auth(access_key, secret_key)
# 初始化BucketManager
bucket = BucketManager(q)
# 有两种方式构造base_url的形式
base_url = 'http://%s/%s' % (bucket_domain, phone_name)
# 或者直接输入url的方式下载
base_url = 'http://domain/key'
# 可以设置token过期时间
private_url = q.private_download_url(base_url, expires=3600)
print(private_url)
r = requests.get(private_url)
assert r.status_code == 200
# 获取随机颜色
def get_random_color(length):
return (random.randint(0,255),random.ranint(0,255),random.randint(0,255))
# 生成图片
def generate_image(length):
s = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRTSUVWXYZ1234567890'
size = (130,50)
# 创建画布
im = Image.new('RGB',size,color=get_random_color())
# 创建字体
ImageFont.truetype('font/segoerp.ttf',size=35)
# 创建ImageDraw对象
draw = ImageDraw.Draw(im)
code = ''
# 绘制验证码
for i in random(length):
c = random.choice(s)
code += c
# draw.text(坐标位置,绘制谁,用什么颜色,字体)
draw.text((5+ random.randint(4,7) + 25 * i, random.randint(1,4)),
text=c,fill=get_random_color(),
font=font)
# 绘制干扰线
for i in range(8):
x1 = random.randomint(0,130)
y1 = random.randomint(0,50/2)
x2 = random.randomint(0, 130)
y2 = random.randomint(50/2, 130)
draw.line(((x1,y1),(x2,y2)),fill=get_random_color())
im = im.filter(ImageFilter.EDGE_ENHANCE)
return im, code
部分html视图代码,其余html文件可根据自身要求编写
register.html
{% extends "base.html" %}
{% block title %}用户注册{% endblock %}
{% block styles %}
{{ super() }}
<style>
#container{
width: 1000px ;
height: 600px;
margin: 20px auto;
padding-top: 20px;
}
style>
{% endblock %}
{% block newcontent %}
<div id="container">
<h1 class="col-sm-offset-4 col-sm-10">用户注册h1>
<p class="col-sm-offset-4 col-sm-10" style="color:red">{{ msg }}p>
<form class="form-horizontal" method="post" action="{{ url_for('users.register') }}">
<div class="form-group">
<label for="inputUsername" class="col-md-2 control-label">用户名label>
<div class="col-md-6">
<input type="text" class="form-control" id="inputUsername" name='username' placeholder="username">
div>
div>
<div class="form-group">
<label for="inputPassword" class="col-md-2 control-label">密码label>
<div class="col-md-6">
<input type="password" class="form-control" id="inputPassword" name="password" placeholder="password">
div>
div>
<div class="form-group">
<label for="inputConfirm" class="col-md-2 control-label">确认密码label>
<div class="col-md-6">
<input type="password" class="form-control" id="inputConfirm" name="repassword" placeholder="confirm password">
div>
div>
<div class="form-group">
<label for="inputPhone" class="col-md-2 control-label">手机号码label>
<div class="col-md-6">
<input type="password" class="form-control" id="inputPhone" name="phone" placeholder="phone number"><span id="">span>
div>
div>
<div class="form-group">
<label for="inputEmail" class="col-md-2 control-label">邮箱label>
<div class="col-md-6">
<input type="email" class="form-control" id="inputEmail" name="email" placeholder="Email">
div>
div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-12">
<button type="submit" class="btn btn-primary col-sm-3" >注 册button>
div>
div>
form>
div>
{% endblock %}
{# 添加js脚本 #}
{% block scripts %}
{{ super() }}
<script>
$('#inputPhone').blur(function(){
let phone = $(this).val();
let span_ele = $(this).next('span');
if(phone.length==11){
span_ele.text('');
$.get('{{ url_for('users.check_phone') }}',{phone:phone},function(data){
console.log(data);
if(data.code!=200){
span_ele.css({'color':'#ff0011','font-size':'12px'});
span_ele.text('data.msg');
}
})
} else{
span_ele.css({'color':'#ff0011','font-size':'12px'});
span_ele.text('手机格式错误');
}
})
script>
{% endblock %}
login.html
{% extends "base.html" %}
{% block title %}用户登录{% endblock %}
{% block styles %}
{{ super() }}
<style>
#container{
width: 1000px ;
height: 600px;
margin: 0px auto;
padding-top: 30px;
}
#container h1 {
text-align: center;
margin-bottom: 50px;
}
#container form {
margin: 0 auto;
}
#tab{
font-size: 20px;
text-align: left;
padding-left: 170px;
margin-bottom: 50px;
}
#tab span{
display: inline-block;
width: 120px;
text-align: center;
cursor: default;
color: #f3f3f3;
}
#tips{
color: red;
font-size: 10px;
}
#btnCheck{
width: 140px;
}
style>
{% endblock %}
{% block newcontent %}
<div id="container">
<h1 class="col-sm-offset-4 col-sm-10">用户登录h1>
<div id="tab">
<span>用户名/密码span> | <span>手机验证码span>
<p id="tips">{{ msg }}p>
div>
<div class="logintab">
<form class="form-horizontal" method="post" action="{{ url_for('users.login') }}?f=1">
<div class="form-group">
<label for="inputUsername" class="col-md-2 col-sm-offset-1 control-label">用户名label>
<div class="col-md-6">
<input type="text" class="form-control" id="inputUsername" name='username' placeholder="username">
div>
div>
<div class="form-group">
<label for="inputPassword" class="col-md-2 col-sm-offset-1 control-label">密码label>
<div class="col-md-6">
<input type="password" class="form-control" id="inputPassword" name="password" placeholder="password">
div>
div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-10">
<button type="submit" class="btn btn-primary col-sm-3" >登 录button>
<button type="submit" class="btn btn-primary col-sm-3" >重 置button>
div>
div>
form>
div>
<div class="logintab">
<form class="form-horizontal" method="post" action="{{ url_for('users.login') }}?f=2">
<div class="form-group">
<div class="col-md-6 col-md-offset-2">
<input type="text" class="form-control" id="inputPhone" name='phone' placeholder="输入手机号">
div>
div>
<div class="form-group">
<div class="col-md-4 col-md-offset-2">
<input type="text" class="form-control" id="inputCode" name="code" placeholder="输入验证码">
div>
<div class="col-md-2">
<input type="button" id="btnCheck" class="btn btn-info" value="发送验证码">
div>
div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-primary col-sm-3" >登 录button>
<button type="submit" class="btn btn-primary col-sm-3" >重 置button>
div>
div>
form>
div>
div>
{% endblock %}
{# 添加js脚本 #}
{% block scripts %}
{{ super() }}
<script>
$(function(){
// 显示 | 隐藏 登录方式
$(".logintab").hide();
$(".logintab").first().show();
$("#tab span").each(function(i){
$(this).click(function(){
$(".logintab").hide();
$(".logintab").eq(i).show();
})
});
// 发送验证码
$('#btnCheck').click(function(){
let phone = $('#inputPhone').val();
let span_obj = $('#inputPhone').next('span');
if(phone && phone.length==11){
alert(phone)
// 发送ajax
$.get('{{ url_for('users.send_message') }}',{phone:phone},function(data){
if(data.code==200){
alert('短信发送成功,请注意查收!');
}else{
alert(data.msg);
}
});
}else{
span_obj.css({'color':'red','font-size':'12px'});
span_obj.text('必须输入11为手机号码')
}
});
})
script>
{% endblock %}
index.html
{% extends "base.html" %}
{% block title %}博客首页{% endblock %}
{% block styles %}
{{ super() }}
<style>
#container{
width: 1200px ;
margin: 0 auto;
}
.article{
width: 90%;
margin: 20px auto;
padding: 5px 10px;
background-color: rgba(131,202,227,0.7);
border-bottom: 1px solid Linen;
}
.article p {
font-size: 14px;
rgba(75,75,74,1.00);
}
.article h4 a {
color: rgba(80,80,79,1.00);
}
.article h4 a:hover {
color: black;
}
.article div span {
margin-left: 20px;
}
.article div{
margin-bottom: 18px;
}
</style>
{% endblock %}
{% block newcontent %}
<div id="container">
{% for article in pagination.items %}
<div class="article">
<h4><a href="{{ url_for('articles.articles_detail')}}?aid={{ article.id }}">{{ article.title }}</a></h4>
<p>
<span>作者:{{ article.user.username }}</span>
<br>
<br>
<div>{{ article.content | safe }}</div>
</p>
<div>
<span style="margin-left:0">发布时间:{{ article.pdatetime }}</span>
<span>收藏:{{ article.save_num }}</span>
</div>
</div>
{% endfor %}
<!--
<div class="article"></div>
<div class="article"></div>
<div class="article"></div>
<div class="article"></div>
<div class="article"></div>
-->
<nav aria-label="Page navigation" class="col-md-offset-5">
<ul class="pagination">
<li {% if not pagination.has_prev %} class="disabled" {% endif %}>
<a {% if pagination.has_prev %} href="{{ url_for('users.index') }}?page={{ pagination.prev_num }} " {% endif %} aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<!--
<li {% if not pagination.has_prev %} class="disabled" {% endif %}>
<a href="{{ url_for('users.index') }}?page={{ pagination.prev_num }} " aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
-->
{% for page_num in range(1,pagination.pages + 1) %}
<li {% if pagination.page== page_num %} class="active" {% endif %}><a href="{{url_for('users.index') }}?page={{ page_num }}">{{ page_num }}</a> </li>
{% endfor %}
<!-- <li class="active"><a href="{{ url_for('users.index') }}?page=1">1 <span class="sr-only">(current)</span></a></li>
<li><a href="{{ url_for('users.index') }}?page=3">3 <span class="sr-only">(current)</span></a></li>
<li><a href="{{ url_for('users.index') }}?page=4">4 <span class="sr-only">(current)</span></a></li> -->
<li {% if not pagination.has_next %} class="disabled" {% endif %}>
<a {% if pagination.has_next %} href="{{ url_for('users.index') }}?page={{ pagination.next_num }}" {% endif %} aria-label="next">
<span aria-hidden="true">«</span>
</a>
</li>
</ul>
</nav>
</div>
{% endblock %}
board.html
{% extends 'base.html' %}
{% block title %}
文章详情
{% endblock %}
{% block styles %}
{{ super() }}
<style xmlns="http://www.w3.org/1999/html">
#container{
width: 1200px;
margin: 0 auto;
}
#detail p{
background-color: rgba(232,243,242,0.6);
border-radius: 10px;
margin-top: 20px;
margin-bottom: 20px;
padding: 20px 50px;
}
#detail h2{
text-align: center;
}
style>
{% endblock %}
{% block newcontent %}
<div id="container">
<div id="detail">
<div id="comment">
<div>
<form action="{{ url_for('users.show_board') }}" method="post">
<p>
用户留言:
<br>
<br>
<textarea name="board" placeholder="写下你的想说的,开始我们的对话" class="form-control" style="..." rows="5" cols="10">textarea>
<br>
<input type="submit" value="发表留言" class="btn btn-info">
p>
form>
div>
p>
<div id="comment_item">
{% if boardes %}
{% for board in boardes.item %}
<div class="item">
<div id="item_left">
<img src="{% if board.user.icon %}{{ url_for('static',filename=board.user.icon) }} {% else %}{{ url_for('static',filename='images/touxiang.jpg') }}{% endif %}" alt="">
div>
<div id="item_right">
<p>
<span>
{% if not board.user_id %}
匿名用户
{% else %}
{{ board.user.username }}
{% endif %}
span>
<span>{{ board.mdatetime }}span>
p>
<p>{{ border.comment }}p>
div>
div>
{% endfor %}
{% else %}
还没有任何的留言,赶快发表吧!
{% endif %}
div>
<div id="paginate">
<nav aria-label="...">
<ul class="pager">
<li class="previous {% if not boardes.has_prev %}disabled{% endif %}">
<a {% if boardes.has_prev %} href="{{ url_for('users.show_board') }}?page={{ boardes.prev_num }}" {% endif %}><span aria-hidden="true">←span> 上一页a>li>
<li class="next {% if not boardes.has_next %}disabled{% endif %}">
<a {% if boardes.has_next %} href="{{ url_for('users.show_board') }}?page={{ boardes.next_num }}" {% endif %}>下一页 <span aria-hidden="true">→span>a>li>
ul>
nav>
div>
div>
div>
div>
{% endblock %}
{% block script %}
{{ super() }}
<script>
$(function(){
//文本域
$('textarea[name="comment"]').focus(function(){
$(this).val("")
})
})
script>
{% endblock %}
base.html
{% extends "bootstrap/base.html" %}
{% block title %}首页{% endblock %}
{% block styles %}
{{ super() }}
<style>
body{
background-image: url("{{ url_for('static',filename='images/bg.jpg') }}");
background-size: contain|cover;
width: 100%;
height: 100%;
}
body background-image{
}
#myfoot{
border-top: 1px solid Black;
font-size: 14px;
text-align: center;
color: Black;
}
.navbar{
}
.container {
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
.footer {
padding: 30px 0;
border-top: 1px solid #e5e5e5;
margin-top: 70px;
}
style>
{% endblock %}
{% block navbar %}
<nav class="navbar navbar-default ">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="{{ url_for('users.index') }}">个人博客a>
div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{{ url_for('users.show_about')}}">关于我a>li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">文章类型 <span class="caret">span>a>
<ul class="dropdown-menu">
{% for type in types %}
{% if not loop.last %}
<li><a href="{{ url_for('articles.type_search') }}?tid={{type.id}}">{{ type.type_name }}a>li>
<li role="separator" class="divider">li>
{% else %}
<li><a href="{{ url_for('articles.type_search') }}?tid={{type.id}}">{{ type.type_name }}a>li>
{% endif %}
{% endfor %}
ul>
li>
<li><a href="{{ url_for('users.myphoto') }}">我的相册a>li>
<li><a href="{{ url_for('users.show_board') }}">留言板a>li>
ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="搜索">
div>
<button type="submit" class="btn btn-default">Submitbutton>
form>
<ul class="nav navbar-nav navbar-right">
{% if user %}
<li><img
src="{% if user.icon %} {{ url_for('static',filename=user.icon) }} {% else %} {{ url_for('static',filename='images/touxiang.png') }}{% endif %}"
alt="" style="border-radius:15px;margin-top: 8px;width: 30px;height: 30px">li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">欢迎!{{ user.username }} <span class="caret">span>a>
<ul class="dropdown-menu">
<li><a href="{{ url_for('users.center') }}">用户中心a>li>
<li><a href="{{ url_for('users.logout') }}">退出a>li>
ul>
li>
{% else %}
<li><a href="{{url_for('users.login')}}">登录a>li>
<li><a href="{{url_for('users.register')}}">注册a>li>
{% endif %}
ul>
div>
div>
nav>
{% endblock %}
{% block content %}
{% block newcontent %}
<h1>Hello,Bootstraph1>
{% endblock %}
{% block footer %}
<div class="container">
<div class="col-md-6 col-sm-offset-2 col-lg-6">
<div class="row about">
<div class="col-sm-3">
<h4>关于h4>
<ul class="list-unstyled">
<li><a href="/about/">关于我们a>li>
<li><a href="/ad/">广告合作a>li>
<li><a href="/links/">友情链接a>li>
<li><a href="/hr/">招聘a>li>
ul>
div>
<div class="col-sm-3">
<h4>联系方式h4>
<ul class="list-unstyled">
<li><a href="https://weibo.com/bootcss" title="Bootstrap中文网官方微博" target="_blank">新浪微博a>li>
<li><a href="mailto:admin@bootcss.com">电子邮件a>li>
ul>
div>
<div class="col-sm-3">
<h4>旗下网站h4>
<ul class="list-unstyled">
<li><a href="https://www.bootcdn.cn/" target="_blank">BootCDNa>li>
<li><a href="https://pkg.phpcomposer.com/" target="_blank">Packagist中国镜像a>li>
ul>
div>
<div class="col-sm-3">
<h4>特别致谢h4>
<ul class="list-unstyled">
<li><a href="https://www.maoyun.com/" target="_blank">猫云a>li>
ul>
div>
div>
div>
div>
<hr>
<div class="row footer-bottom">
<ul class="list-inline text-center">
<li><a href="https://beian.miit.gov.cn/" target="_blank">京ICP备11008151号-6a>li><li>京公网安备11010802014853li>
ul>
div>
div>
{% endblock %}
{% endblock %}
settings.py
import os
class Config:
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'mysql://root:123456@127.0.0.1:3306/flaskblog'
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_ECHO = True
# secret_key加密码
SECRET_KEY = 'JIAMIMA'
RECAPTCHA_PUBLIC_KEY = "********"
RECAPTCHA_PRIVATE_KEY = "***********"
RECAPTCHA_PARAMETERS = {'hl':'zh','render':'explicit'}
RECAPTCHA_DATA_ATTRS = {'theme':'dark'}
# 项目路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 静态文件夹的路径
STATIC_DIR = os.path.join(BASE_DIR,'static')
TEMPLATE_DIR = os.path.join(BASE_DIR,'templates')
# 头像的上传目录
UPLOAD_ICON_DIR = os.path.join(STATIC_DIR,'upload/icon')
# 相册的上传目录
UPLOAD_PHONE_DIR = os.path.join(STATIC_DIR,'upload/phone')
class DevelopmentConfig(Config):
ENV = 'development'
DEBUG = True
class ProductionConfig(Config):
ENV = 'production'
DEBUG = True
app.py
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from flask_wtf import CSRFProtect
from apps.user.models import *
from apps.article.models import *
from apps.goods.models import *
from apps import create_app
from exts import db
app = create_app()
manager = Manager(app = app)
migrate = Migrate(app = app, db = db)
manager.add_command('db',MigrateCommand)
# csrf = CSRFProtect(app=app)
@manager.command
def init():
print('初始化')
if __name__ == '__main__':
manager.run()
apps下的init.py
from flask import Flask
import settings
from apps.article.view import article_bp
from apps.article.views import articles_bp
from apps.goods.view import goods_bp
from apps.user.view import user_bp
from apps.user.views import users_bp
from exts import db, bootstrap, cache
config = {
'CACH_TYPE': 'REDIS',
'CACHE_REDIS_HOST': '127.0.0.1',
'CACHE_REDIS_PORT': 6379
# 'CACHE_REDIS_PASSWORD':'',
# 'CACHE_REDIS_DB':''
}
def create_app():
app = Flask(__name__,template_folder='../templates',static_folder='../static')
app.config.from_object(settings.DevelopmentConfig)
# 初始化db
db.init_app(app)
bootstrap.init_app(app)
cache.init_app(app = app,config = config)
# app.register_blueprint(user_bp)
app.register_blueprint(users_bp)
app.register_blueprint(article_bp)
app.register_blueprint(articles_bp)
app.register_blueprint(goods_bp)
return app
运行结果: