一.程序包

  程序包用来保存程序的所有代码、模板和静态文件。我们可以把这个包直接称为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从入门到精通之大型程序的结构二的更多相关文章

  1. Flask从入门到精通之大型程序的结构一

    尽管在单一脚本中编写小型Web 程序很方便,但这种方法并不能广泛使用.程序变复杂后,使用单个大型源码文件会导致很多问题.不同于大多数其他的Web 框架,Flask 并不强制要求大型项目使用特定的组织方 ...

  2. Flask从入门到精通之flask程序入门

    初始化 所有Flask程序都必须创建一个程序实例,Web服务器使用一种名为Web服务器网关接口的的协议(WSGI),把接收自客户端的所有请求转发给这个对象处理.程序实例是Flask类的对象,使用下面代 ...

  3. [ Python ] Flask 基于 Web开发 大型程序的结构实例解析

    作为一个编程入门新手,Flask是我接触到的第一个Web框架.想要深入学习,就从<FlaskWeb开发:基于Python的Web应用开发实战>这本书入手,本书由于是翻译过来的中文版,理解起 ...

  4. Flask从入门到精通之Flask-Bootstrap的使用

    Bootstrap(http://getbootstrap.com/)是Twitter 开发的一个开源框架,它提供的用户界面组件可用于创建整洁且具有吸引力的网页,而且这些网页还能兼容所有现代Web 浏 ...

  5. Flask从入门到精通之使用Flask-Migrate实现数据库迁移

    在开发程序的过程中,你会发现有时需要修改数据库模型,而且修改之后还需要更新数据库.仅当数据库表不存在时,Flask-SQLAlchemy 才会根据模型进行创建.因此,更新表的唯一方式就是先删除旧表,不 ...

  6. Flask从入门到精通之使用Flask-SQLAlchemy管理数据库

    Flask-SQLAlchemy 是一个Flask 扩展,简化了在Flask 程序中使用SQLAlchemy 的操作.SQLAlchemy 是一个很强大的关系型数据库框架,支持多种数据库后台.SQLA ...

  7. Flask从入门到精通之Flash消息

    请求完成后,有时需要让用户知道状态发生了变化.这里可以使用确认消息.警告或者错误提醒.一个典型例子是,用户提交了有一项错误的登录表单后,服务器发回的响应重新渲染了登录表单,并在表单上面显示一个消息,提 ...

  8. Flask从入门到精通之重定向和用户会话

    最新版的hello.py 存在一个可用性问题.用户输入名字后提交表单,然后点击浏览器的刷新按钮,会看到一个莫名其妙的警告,要求在再次提交表单之前进行确认.之所以出现这种情况,是因为刷新页面时浏览器会重 ...

  9. Flask从入门到精通之跨站请求伪造保护

    默认情况下,Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,CSRF)的攻击.恶意网站把请求发送到被攻击者已登录的其他网站时就会引发CSRF ...

随机推荐

  1. jQuery 用$.param(json) 将 Json 转换为 Url queryString

    如: var params = { param1: 'bar', param2: 'foo' }; var queryString = $.param(params); // queryString ...

  2. mysql添加注释

    -- 查看字段类型-- show columns from campaign_distribute --给表添加注释 -- alter table campaign_distribute commen ...

  3. bitnami redmine svn配置

    采用bitnami 方案安装redmine svn服务器端会自己进行安装 1.创建版本库 首先进入remine安装目录的subversion/bin目录,例如我的安装目录是“/opt/redmine/ ...

  4. fastjson解析List对象

    List<String[]> body = JSON.parseObject(msg.getBody().toString(), new TypeToken<List<Stri ...

  5. ubuntu系统下安装pyspider:使用supervisord启动并管理pyspider进程配置及说明

    首先感谢segmentfault.com的“imperat0r_”用户的文章和新浪的“小菜一碟”用户的文章.这是他们的配置文件.我参考也写了一个,在最后呢. 重点说明写在前面.本人用superviso ...

  6. MYSQL 问题小总结

    mysql 问题小总结 1.MySQL远程连接ERROR 2003(HY000):Can't connect to MySQL server on ‘ip’(111)的问题 通常是mysql配置文件中 ...

  7. Spring 集成 MemCache

    1)xml <bean class="com.danga.MemCached.SockIOPool" factory-method="getInstance&quo ...

  8. 201709011工作日记--Volley源码详解(二)

    1.Cache接口和DiskBasedCache实现类 首先,DiskBasedCache类是Cache接口的实现类,因此我们需要先把Cache接口中的方法搞明白. 首先分析下Cache接口中的东西, ...

  9. (网络流)ACM Computer Factory --POJ --3436

    链接: http://poj.org/problem?id=3436 http://acm.hust.edu.cn/vjudge/contest/view.action?cid=82835#probl ...

  10. 2013多校联合3 G The Unsolvable Problem(hdu 4627)

    2013-07-30 20:35 388人阅读 评论(0) 收藏 举报 http://acm.hdu.edu.cn/showproblem.php?pid=4627 The Unsolvable Pr ...