使用Python的Flask框架构建大型Web应用程序的结构示例
|
虽然小型web应用程序用单个脚本可以很方便,但这种方法却不能很好地扩展。随着应用变得复杂,在单个大的源文件中处理会变得问题重重。 与大多数其他web框架不同,Flask对大型项目没有特定的组织方式;应用程序的结构完全交给开发人员自己决定。在这一章,提出一个可能的方式来组织管理一个大型应用程序的包和模块。这种结构将用于书中其余的示例中。 1、项目结构 示例 基本多文件Flask应用结构 |-flasky |-app/ |-templates/ |-static/ |-main/ |-__init__.py |-errors.py |-forms.py |-views.py |-__init__.py |-email.py |-models.py |-migrations/ |-tests/ |-__init__.py |-test*.py |-venv/ |-requirements.txt |-config.py |-manage.py 这个结构有四个顶层目录:
还有一些新的文件:
为了帮助你完全理解这个结构,下面会描述将hello.py应用改为符合这一结构的整个流程。 2、配置选项 我们可以使用配置类的层次结构来代替hello.py中的简单类字典结构配置。下面展示了config.py文件。 config.py:应用程序配置
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or
'sqlite:///' + os.path.join(basedir,'data-dev.sqlite')
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or
'sqlite:///' + os.path.join(basedir,'data-test.sqlite')
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or
'sqlite:///' + os.path.join(basedir,'data.sqlite')
config = {
'development': DevelopmentConfig,'testing': TestingConfig,'production': ProductionConfig,'default': DevelopmentConfig
}
Config基类包含一些相同配置;不同的子类定义不同的配置。额外配置可以在需要的时候在加入。 为了让配置更灵活更安全,一些设置可以从环境变量中导入。例如,SECRET_KEY,由于它的敏感性,可以在环境中设置,但如果环境中没有定义就必须提供一个默认值。 在三个配置中SQLALCHEMY_DATABASE_URI变量可以分配不同的值。这样应用程序可以在不同的配置下运行,每个可以使用不同的数据库。 配置类可以定义一个将应用程序实例作为参数的init_app()静态方法。这里特定于配置的初始化是可以执行的。这里Config基类实现一个空init_app()方法。 在配置脚本的底部,这些不同的配置是注册在配置字典中。将其中一个配置(开发配置)注册为默认配置。 3、应用程序包 3.1、使用一个应用程序工厂 在单个文件中创建应用程序的方式非常方便,但是它有一个大缺点。因为应用程序创建在全局范围,没有办法动态的适应应用配置的更改:脚本运行时,应用程序实例已经创建,所以它已经来不及更改配置。对于单元测试这是特别重要的,因为有时需要在不同的配置下运行应用程序来获得更好的测试覆盖率。 解决这一问题的方法就是将应用程序放入一个工厂函数中来延迟创建,这样就可以从脚本中显式的调用。 这不仅给脚本充足的时间来设置配置,也能用于创建多个应用程序实例――一些在测试过程中非常有用的东西。被定义在app包的构造函数中的应用程序工厂函数会在示例7-3中展示。 这个构造函数导入大部分当前需要使用的扩展,但因为没有应用程序实例初始化它们,它可以被创建但不初始化通过不传递参数给它们的构造函数。create_app()即应用程序工厂函数,需要传入用于应用程序的配置名。配置中的设置被保存在config.py中的一个类中,可以使用Flask的app.config配置对象的from_object()方法来直接导入。配置对象可以通过对象名从config字典中选出。一旦应用程序被创建且配置好,扩展就可以被初始化。调用扩展里的init_app()之前先创建并完成初始化工作。 app/ _init__.py:应用程序包构造函数_ from flask import Flask,render_template from flask.ext.bootstrap import Bootstrap from flask.ext.mail import Mail from flask.ext.moment import Moment from flask.ext.sqlalchemy import SQLAlchemy from config import config bootstrap = Bootstrap() mail = Mail() moment = Moment() db = SQLAlchemy() 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) # attach routes and custom error pages here return app 工厂函数返回创建的应用程序实例,但是请注意,在当前状态下使用工厂函数创建的应用程序是不完整的,因为它们没有路由和自定义错误页面处理程序。这是下一节的主题。 3.2、在蓝图中实现应用程序的功能 应用程序工厂的转化工作引出了路由的复杂化。在单脚本应用中,应用程序实例是全局的,所以可以很容易地使用app.route装饰器定义路由。但是现在应用程序在运行时创建,app.route装饰器只有在create_app()调用后才开始存在,这就太迟了。就像路由那样,这些通过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/init.py脚本的底部导入模块要避免循环依赖,因为view.py和errors.py都需要导入main蓝图。 蓝图和应用程序一样注册在create_app()工厂函数中,如下所示。 示例 app/ _init__.py:蓝图注册_ def create_app(config_name): # ... from .main import main as main_blueprint app.register_blueprint(main_blueprint) return app 下面则展示了错误处理。 app/main/errors.py:蓝图的错误处理
from flask import render_template
from . import main
@main.app_errorhandler(404)
def page_not_found(e):
return render_template('404.html'),404
@main.app_errorhandler(500)
def internal_server_error(e):
return render_template('500.html'),500
在蓝图中写错误处理的不同之处是,如果使用了errorhandler装饰器,则只会调用在蓝图中引起的错误处理。而应用程序范围内的错误处理则必须使用app_errorhandler。 这里展示了被更新在蓝图中的应用程序路由。 app/main/views.py:带有蓝图的应用程序路由
from datetime import datetime
from flask import render_template,session,redirect,url_for
from . import main
from .forms import NameForm
from .. import db
from ..models import User
@main.route('/',methods=['GET','POST'])
def index():
form = NameForm()
if form.validate_on_submit():
# ...
return redirect(url_for('.index'))
return render_template('index.html',form=form,name=session.get('name'),known=session.get('known',False),current_time=datetime.utcnow())
(编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
