Flask从入门到精通之大型程序的结构二
一.程序包
程序包用来保存程序的所有代码、模板和静态文件。我们可以把这个包直接称为app(应用),如果有需求,也可使用一个程序专用名字。templates 和static 文件夹是程序包的一部分,因此这两个文件夹被移到了app 中。数据库模型和电子邮件支持函数也被移到了这个包中,分别保存为app/models.py 和app/email.py。
使用程序工厂函数
在单个文件中开发程序很方便,但却有个很大的缺点,因为程序在全局作用域中创建,所以无法动态修改配置。运行脚本时,程序实例已经创建,再修改配置为时已晚。这一点对单元测试尤其重要,因为有时为了提高测试覆盖度,必须在不同的配置环境中运行程序。
这个问题的解决方法是延迟创建程序实例,把创建过程移到可显式调用的工厂函数中。这种方法不仅可以给脚本留出配置程序的时间,还能够创建多个程序实例,这些实例有时在测试中非常有用。程序的工厂函数在app 包的构造文件中定义,如下所示:
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_pagedown import PageDown
from config import config bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
pagedown = PageDown() login_manager = LoginManager()
login_manager.login_view = 'auth.login' def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app) bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
login_manager.init_app(app)
pagedown.init_app(app)
...
return app
构造文件导入了大多数正在使用的Flask 扩展。由于尚未初始化所需的程序实例,所以没有初始化扩展,创建扩展类时没有向构造函数传入参数。create_app() 函数就是程序的工厂函数,接受一个参数,是程序使用的配置名。配置类在config.py 文件中定义,其中保存
的配置可以使用Flask app.config 配置对象提供的from_object() 方法直接导入程序。至于配置对象,则可以通过名字从config 字典中选择。程序创建并配置好后,就能初始化扩展了。在之前创建的扩展对象上调用init_app() 可以完成初始化过程。
在蓝本中实现程序功能
转换成程序工厂函数的操作让定义路由变复杂了。在单脚本程序中,程序实例存在于全局作用域中,路由可以直接使用app.route 修饰器定义。但现在程序在运行时创建,只有调用create_app() 之后才能使用app.route 修饰器,这时定义路由就太晚了。和路由一样,自定义的错误页面处理程序也面临相同的困难,因为错误页面处理程序使用app.errorhandler 修饰器定义。
幸好Flask 使用蓝本提供了更好的解决方法。蓝本和程序类似,也可以定义路由。不同的是,在蓝本中定义的路由处于休眠状态,直到蓝本注册到程序上后,路由才真正成为程序的一部分。使用位于全局作用域中的蓝本时,定义路由的方法几乎和单脚本程序一样。和程序一样,蓝本可以在单个文件中定义,也可使用更结构化的方式在包中的多个模块中创建。为了获得最大的灵活性,程序包中创建了一个子包,用于保存蓝本。下面的例子是这个子包的构造文件,蓝本就创建于此。
app/main/__init__.py:创建蓝本
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
通过实例化一个Blueprint 类对象可以创建蓝本。这个构造函数有两个必须指定的参数:蓝本的名字和蓝本所在的包或模块。和程序一样,大多数情况下第二个参数使用Python 的__name__ 变量即可。程序的路由保存在包里的app/main/views.py 模块中,而错误处理程序保存在app/main/errors.py 模块中。导入这两个模块就能把路由和错误处理程序与蓝本关联起来。注意,这些模块在app/main/__init__.py 脚本的末尾导入,这是为了避免循环导入依赖,因为在views.py 和errors.py 中还要导入蓝本main。
蓝本在工厂函数create_app() 中注册到程序上:
def create_app(config_name):
from .main import main as main_blueprint
app.register_blueprint(main_blueprint) from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth') from .api import api as api_blueprint
app.register_blueprint(api_blueprint, url_prefix='/api/v1') return app
在蓝本中编写错误处理程序稍有不同,如果使用errorhandler 修饰器,那么只有蓝本中的错误才能触发处理程序。要想注册程序全局的错误处理程序,必须使用app_errorhandler
from flask import render_template, request, jsonify
from . import main @main.app_errorhandler()
def forbidden(e):
if request.accept_mimetypes.accept_json and \
not request.accept_mimetypes.accept_html:
response = jsonify({'error': 'forbidden'})
response.status_code =
return response
return render_template('403.html'),
在蓝本中编写视图函数主要有两点不同:第一,和前面的错误处理程序一样,路由修饰器由蓝本提供;第二,url_for() 函数的用法不同。你可能还记得,url_for() 函数的第一个参数是路由的端点名,在程序的路由中,默认为视图函数的名字。例如,在单脚本程序
中,index() 视图函数的URL 可使用url_for('index') 获取。在蓝本中就不一样了,Flask 会为蓝本中的全部端点加上一个命名空间,这样就可以在不同的蓝本中使用相同的端点名定义视图函数,而不会产生冲突。命名空间就是蓝本的名字(Blueprint 构造函数的第一个参数),所以视图函数index() 注册的端点名是main.index,其URL 使用url_for('main.index') 获取。
url_for() 函数还支持一种简写的端点形式,在蓝本中可以省略蓝本名,例如url_for('.index')。在这种写法中,命名空间是当前请求所在的蓝本。这意味着同一蓝本中的重定向可以使用简写形式,但跨蓝本的重定向必须使用带有命名空间的端点名。
@main.route('/', methods=['GET', 'POST'])
def index():
form = PostForm()
if current_user.can(Permission.WRITE) and form.validate_on_submit():
post = Post(body=form.body.data,
author=current_user._get_current_object())
db.session.add(post)
db.session.commit()
return redirect(url_for('.index'))
Flask从入门到精通之大型程序的结构二的更多相关文章
- Flask从入门到精通之大型程序的结构一
尽管在单一脚本中编写小型Web 程序很方便,但这种方法并不能广泛使用.程序变复杂后,使用单个大型源码文件会导致很多问题.不同于大多数其他的Web 框架,Flask 并不强制要求大型项目使用特定的组织方 ...
- Flask从入门到精通之flask程序入门
初始化 所有Flask程序都必须创建一个程序实例,Web服务器使用一种名为Web服务器网关接口的的协议(WSGI),把接收自客户端的所有请求转发给这个对象处理.程序实例是Flask类的对象,使用下面代 ...
- [ Python ] Flask 基于 Web开发 大型程序的结构实例解析
作为一个编程入门新手,Flask是我接触到的第一个Web框架.想要深入学习,就从<FlaskWeb开发:基于Python的Web应用开发实战>这本书入手,本书由于是翻译过来的中文版,理解起 ...
- Flask从入门到精通之Flask-Bootstrap的使用
Bootstrap(http://getbootstrap.com/)是Twitter 开发的一个开源框架,它提供的用户界面组件可用于创建整洁且具有吸引力的网页,而且这些网页还能兼容所有现代Web 浏 ...
- Flask从入门到精通之使用Flask-Migrate实现数据库迁移
在开发程序的过程中,你会发现有时需要修改数据库模型,而且修改之后还需要更新数据库.仅当数据库表不存在时,Flask-SQLAlchemy 才会根据模型进行创建.因此,更新表的唯一方式就是先删除旧表,不 ...
- Flask从入门到精通之使用Flask-SQLAlchemy管理数据库
Flask-SQLAlchemy 是一个Flask 扩展,简化了在Flask 程序中使用SQLAlchemy 的操作.SQLAlchemy 是一个很强大的关系型数据库框架,支持多种数据库后台.SQLA ...
- Flask从入门到精通之Flash消息
请求完成后,有时需要让用户知道状态发生了变化.这里可以使用确认消息.警告或者错误提醒.一个典型例子是,用户提交了有一项错误的登录表单后,服务器发回的响应重新渲染了登录表单,并在表单上面显示一个消息,提 ...
- Flask从入门到精通之重定向和用户会话
最新版的hello.py 存在一个可用性问题.用户输入名字后提交表单,然后点击浏览器的刷新按钮,会看到一个莫名其妙的警告,要求在再次提交表单之前进行确认.之所以出现这种情况,是因为刷新页面时浏览器会重 ...
- Flask从入门到精通之跨站请求伪造保护
默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,CSRF)的攻击.恶意网站把请求发送到被攻击者已登录的其他网站时就会引发CSRF ...
随机推荐
- linux 下安装gult
本文假设你之前没有用过任何任务脚本(task runner)和命令行工具,一步步教你上手Gulp.不要怕,它其实很简单,我会分为五步向你介绍gulp并帮助你完成一些惊人的事情.那就直接开始吧. 第一步 ...
- python不用声明数据类型
不用声明变量一样,Python不用去声明函数的返回类型,是由于其“若类型”的语言特性决定的. 在其他语言中,例如C/C++语言中在存储一个数据之前,都需要在内存中给这个数据开辟一个固定的内存空间,并给 ...
- UI设计规范:单选按钮 vs 复选框,没那么简单
无论是网页设计,还是移动app设计,都经常用到单选按钮和复选框这两个组件.这两个组件看似意义明确,很好区分,但在实际设计中却很容易用错,带来不好的用户体验. 本文中我通过列举几个典型的错误用法,帮助设 ...
- 百度 ueditor 1.2.0 注意事项 ,上传文件问题
<script type="text/javascript" src="script/ueditor/ueditor.config.js" charset ...
- cubieboard网络设置
1.1 配置静态ip vi /etc/network/interface auto lo iface lo inet loopback #以下是添加的内如 auto eth0 #iface eth0 ...
- 关于使用smsx.cab控件做web打印使用方法(转)
注意:在使用之前先告诉下我的痛苦经历 在做WEB项目是我的JSP页面在jsp文件夹里,我把smsx.cab放在js文件夹里(jsp和js是用级别目录) 在本机上测试可以正确下载控件,但是部署到测试服务 ...
- 前端之JavaScript笔记2
一 数组对象 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
- Servlet的Cookie值保存与获取
今天测试设置和获取Cookie遇到了一点小问题,很奇怪的问题:把J2ee服务部署在本地 8080端口:访问任何一个服务时,如果客户端没有cookie,则下发cookie, 如果客户端已经有了该cook ...
- 2018.10.15 loj#6010. 「网络流 24 题」数字梯形(费用流)
传送门 费用流经典题. 按照题目要求建边. 为了方便我将所有格子拆点,三种情况下容量分别为111,infinfinf,infinfinf,费用都为validi,jval_{id_{i,j}}valid ...
- 2018.07.23 洛谷P4513 小白逛公园(线段树)
传送门 线段树常规操作了解一下. 单点修改维护区间最大连续和. 对于一个区间,维护区间从左端点开始的连续最大和,从右端点开始的连续最大和,整个区间最大和,区间和. 代码如下: #include< ...