Flask 应用最佳实践
一个好的应用目录结构可以方便代码的管理和维护,一个好的应用管理维护方式也可以强化程序的可扩展性
应用目录结构
假定我们的应用主目录是”flask-demo”,首先我们建议每个应用都放在一个独立的包下,假设包名是”myapp”。所以,整个应用的目录结构如下:
flask-demo/
├ run.py # 应用启动程序
├ config.py # 环境配置
├ requirements.txt # 列出应用程序依赖的所有Python包
├ tests/ # 测试代码包
│ ├ __init__.py
│ └ test_*.py # 测试用例
└ myapp/
├ admin/ # 蓝图目录
├ static/
│ ├ css/ # css文件目录
│ ├ img/ # 图片文件目录
│ └ js/ # js文件目录
├ templates/ # 模板文件目录
├ __init__.py
├ forms.py # 存放所有表单,如果多,将其变为一个包
├ models.py # 存放所有数据模型,如果多,将其变为一个包
└ views.py # 存放所有视图函数,如果多,将其变为一个包
应用的创建代码放在”myapp/__init__.py”中:
from flask import Flask
from myapp.admin import admin
import config app = Flask(__name__)
app.config.from_object('config')
app.register_blueprint(admin) from myapp import views
我们把创建应用的代码与应用的启动剥离开,并且在应用对象创建之后,再导入视图模块,因为此时视图函数上的”@app.route()”才有效。视图模块包括了所有的视图函数及路由,如果有多个视图模块,则一并导入。在主目录下的”run.py”内,我们才放入应用的启动代码:
from myapp import app app.run(host='0.0.0.0')
此后,应用的启动都是通过执行”run.py”来完成。蓝图里的目录结构跟应用基本上一样,也是”static”目录放所有静态文件,”templates”目录放所有模板文件,”views.py”存放所有视图,”forms.py”存放所有表单,”models.py”存放所有数据模型,我就不画了。蓝图对象的初始化也放在”__init__.py”里,并在初始化后导入蓝图中的视图模块,这样风格可以跟应用保持一致:
from flask import Blueprint
admin = Blueprint('admin', __name__, url_prefix='/admin')
from myapp.admin import views
还有一种风格,就是将蓝图的模板和静态文件也放在myapp的”templates”和”static”目录下,只是在这些目录下创建与蓝图同名的子目录,比如:
flask-demo/
...
└ myapp/
├ admin/ # 蓝图目录
├ static/
│ ├ admin/ # 蓝图admin专有的js, css, 图片文件
│ ├ css/ # css文件目录
│ ├ img/ # 图片文件目录
│ └ js/ # js文件目录
├ templates/ # 模板文件目录
│ └ admin/ # 蓝图admin的模板文件
├ __init__.py
...
这样做的好处就是便于资源统一管理。此时蓝图中获取模板文件或静态文件,都需要加个前缀,比如”render_template(‘admin/hello.html’)”。个人觉得,如果你的蓝图只是为了划分模块,蓝图之间重用部分较多,可以用这个方法。如果蓝图之间比较独立,比如用户站点和管理员后台,就建议采用第一种方法。
应用工厂 App Factory
Flask官方建议采用工厂的模式来创建应用。什么是应用工厂呢?让我们对上例中”myapp”下的”__init__.py”文件作如下修改:
from flask import Flask
from flask_mail import Mail
from flask_sqlalchemy import SQLAlchemy
from werkzeug.utils import import_string mail = Mail()
db = SQLAlchemy() blueprints = [
'myapp.main:main',
'myapp.admin:admin',
] def create_app(config):
app = Flask(__name__)
app.config.from_object(config) # Load extensions
mail.init_app(app)
db.init_app(app) # Load blueprints
for bp_name in blueprints:
bp = import_string(bp_name)
app.register_blueprint(bp) return app
我们不在代码中直接创建应用,而是通过调用”create_app()”方法来返回一个应用对象,这个”create_app()”就是应用工厂方法。在工厂方法里,我们分别加载了配置,扩展和蓝图。这里要注意,因为没有一个全局的app对象,所以上一节例子中的应用视图就无法工作,因为无法执行”@app.route()”。怎么办?还好Flask有个蓝图功能,我们将主程序里的视图,数据模型,表单等也放到一个蓝图下,这里起名为”main”。创建蓝图时不指定”url_prefix”,这样它的URL前缀就是根路径。
from flask import Blueprint
main = Blueprint('main', __name__)
from myapp.main import views
然后在视图函数里用蓝图对象路由即可,这个对象在蓝图创建后就可见了:
from myapp.main import main
@main.route('/')
def index():
return '<h1>Hello World from app factory!</h1>'
有了应用工厂后,我们怎么启动应用呢。这个就简单了,我们修改下主目录下的”run.py”文件:
from myapp import create_app
import config app = create_app('config') app.run(host='0.0.0.0', debug=True)
这样应用就可以被启动了
应用工厂好处如下:
- 在跑自动测试时,每个测试用例都通过应用工厂来获取各自的应用,这样测试用例之前不会互相污染
- 方便获取同一应用的多个实例,比如debug版和release版,并根据需要启用,甚至于同时启用来服务不同的目的
多应用组合
上节最后说到了多个应用(在同一Python进程中)同时启用。这个怎么做到?我们先来深入下Flask中的”app.run()”方法。这个方法本质上是调用了Werkzeug中的”werkzeug.serving.run_simple()”方法来启动一个WSGI服务器,前面例子中”app.run(host=’0.0.0.0′, debug=True)”等同于调用:
from werkzeug.serving import run_simple # debug=True means use_reloader=True and use_debugger=True
run_simple('0.0.0.0', 5000, app, use_reloader=True, use_debugger=True)
我们曾经说过Flask就是基于Werkzeug和Jinja2建立起来的,Werkzeug帮助Flask封装了很多WSGI层面的操作。所以,要同时启用多个应用,就要利用Werkzeug的方法,大家看下面的例子:
from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
from myapp import create_app
import config release_app = create_app('config.release')
debug_app = create_app('config.debug') app = DispatcherMiddleware(release_app, {'/test': debug_app}) run_simple('0.0.0.0', 5000, app, use_reloader=True, use_debugger=True)
我们将这段代码保存在主目录下的”run_batch.py”中,执行它后你会发现。release应用挂在了服务器的根路径上,而debug应用挂在了服务器的”/test”路径上了。这样,我们就实现了两个应用同时启用。”werkzeug.wsgi.DispatcherMiddleware”是一个调度中间件,它的实例化参数就是一组应用及其挂载路径的键值对,没提供挂载路径就意味着挂到根目录上。
此外,”run_simple()”方法只能用于开发环境,生产环境还是建议大家部署在一个强大的Web服务器上
Flask 应用最佳实践的更多相关文章
- flask logging 最佳实践
flask项目中, 你可以使用python 的 logging模块实现记录日志. 也可以使用 flask 基于logging模块封装过的app.logger实现. 直接上代码 config.py im ...
- 在Flask中使用Celery的最佳实践
写在前面 本最佳实践是基于作者有限的经验,欢迎大家共同讨论,可以持续维护此最佳实践.另本文中所使用的环境为Mac&Ubuntu环境,软件版本如下: Celery (4.1.0) Flask ( ...
- python 最佳实践与资源汇总
python 最佳实践 (部分) 一. 结构化工程 文件 功能 README.rst readme LICENSE 许可证 setup.py 打包和发布管理 requirements.txt 开发依赖 ...
- flask 电子邮件进阶实践-用模板发送163邮件
电子邮件进阶实践 下面来学习构建邮件的HTML正文,并使用模板组织内容. 一封电子邮件的正文可以是纯文本(text/plain),也可以是HTML格式的文本(text/html).处于全面的考虑,一封 ...
- 设计 REST API 的13个最佳实践
写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口这件事情就成为了家常便饭,并且,还伴随着无数的争论与无奈.编写友好的 restful api 不论对于你的同事,还是将来作为第三方服 ...
- 13 个设计 REST API 的最佳实践
原文 RESTful API Design: 13 Best Practices to Make Your Users Happy 写在前面 之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口 ...
- Python编程之美:最佳实践指南PDF高清完整版免费下载|百度云盘|Python新手到进阶
百度云盘:Python编程之美:最佳实践指南PDF高清完整版免费下载 提取码:1py6 内容简介 <Python编程之美:最佳实践指南>是Python用户的一本百科式学习指南,由Pytho ...
- ASP.NET跨平台最佳实践
前言 八年的坚持敌不过领导的固执,最终还是不得不阔别已经成为我第二语言的C#,转战Java阵营.有过短暂的失落和迷茫,但技术转型真的没有想象中那么难.回头审视,其实单从语言本身来看,C#确实比Java ...
- 《AngularJS深度剖析与最佳实践》简介
由于年末将至,前阵子一直忙于工作的事务,不得已暂停了微信订阅号的更新,我将会在后续的时间里尽快的继续为大家推送更多的博文.毕竟一个人的力量微薄,精力有限,希望大家能理解,仍然能一如既往的关注和支持sh ...
随机推荐
- 【BZOJ2006】超级钢琴(主席树,优先队列)
[BZOJ2006]超级钢琴(主席树,优先队列) 题面 BZOJ 题解 既然是一段区间 首先就要变成单点 所以求一个前缀和 这个时候贪心很明显了: 枚举每一个点和可以和它组成一段的可行的点 全部丢进一 ...
- HDU1166敌兵布阵(线段树,树状数组)
题面 C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况 ...
- NOIWC2018滚粗记
Day0 \(THUWC\)刚刚结束..顺利的滚粗了 Day1 整理一下自己咸鱼的心 下午到学校坐车出发 这次队伍浩大THUWC只有4个 又到了雅礼洋湖这个地方 路上突然多了许多有关\(NOI\)的标 ...
- [BZOJ1009] [HNOI2008] GT考试 (KMP & dp & 矩阵乘法)
Description 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字. 他的不吉利数学A1A2...Am(0< ...
- 配置linux软件下载跟新地址
/etc/yum.repos.d/CentOS-Base.repo 直接到mirrors.aliyum.com下载 diff:比较
- VS中Ctrl+F5(开始执行不调试)一闪而过问题
昨天学弟学妹在使用VS2015时,遇到了Ctrl+F5运行完毕程序窗口不能停留的问题,感觉比较奇怪.后来了解到他们建立C++解决方案时,直接选中的是空项目,而这样就会导致这样的现象. 想要达到运行完毕 ...
- AJAX跨域问题解决方法(1)——禁止浏览器进行跨域限制
思路:通过命令行修改浏览器启动参数,使得浏览器不进行跨域检查,从而允许跨域 方法:命令行参数启动浏览器后添加参数--disable-web-security 例:chrome --disable-we ...
- 在使用document.getElementById('xxx').files[0]时,关于计算图片大小
在使用文件上传属性时,一直好奇图片上传的大小时如何计算的,最近在使用中认识到的计算方式: 首先,图片大小的存储基本单位是字节(byte).每个字节是由8个比特(bit)组成.所以,一个字节在十进制中 ...
- React 系列文章(1): npm 手动搭建React 运行实例 (新手必看)
摘 要 刚接触React 开发, 在摸索中构建react 运行环境,总会遇到各种坑:本文,将用最短时间解决webpack+react 环境搭建问题. 1.如果你还没有React基础 看这里. 2.如果 ...
- sharepoint REST API 获取文件夹及文件
使用REST操作文件夹: 获取文件夹 url: http://site url/_api/web/GetFolderByServerRelativeUrl('/Shared Documents')/f ...