《Flask development》大型程序的结构

版本库(Repository):你的应用的根目录。这个概念来自于版本控制系统,但在这里有所拓展。当提到“版本库”时,指的是你的项目的根目录。在开发你的应用时,你不太可能会离开这个目录。

包(Package):包含了你的应用代码的一个包。深入探讨以包的形式建立你的应用,但是现在只需知道包是版本库的一个子目录。

模块(Module):一个模块是一个简单的,可以被其它Python文件引入的Python文件。一个包由多个模块组成。

#项目结构规范:
|-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
  • app 目录存放 Flask 程序,包含业务逻辑代码、数据模型和静态文件等
    • configs.py 存放项目配置
    • models 目录存放数据模型文件
    • templates 目录存放模板文件
    • static 目录用于存放静态文件,如 js、css 等文件
  • manage.py 用于启动我们的 Web 程序以及其他的程序任务
  • requirements.txt 文件列出了项目的安装依赖包,便于在其他机器部署
  • migrations 文件夹包含数据库迁移脚本
  • 单元测试编写在 tests 包中
  • venv 文件夹包含 Python 虚拟环境。

配置选项:

程序经常需要设定多个配置。这方面最好的例子就是开发、测试和生产环境要使用不同的数据库,这样才不会彼此影响。

#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
}

在 3 个子类中, SQLALCHEMY_DATABASE_URI 变量都被指定了不同的值。这样程序就可在不同的配置环境中运行,每个环境都使用不同的数据库。
在这个配置脚本末尾, config 字典中注册了不同的配置环境,而且还注册了一个默认配置。

程序包:

  • 工厂方法
    在前面,我们都是直接通过 app=Flask(name) 来创建一个 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]) #使用 Flask app.config 配置对象提供的 from_object() 方法直接导入程序
    config[config_name].init_app(app)

    #在之前创建的扩展对象上调用 init_app() 可以完成初始化过程
    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)

    #注册蓝本
    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')

    return app
  • 蓝本(或称蓝图)
    转换成程序工厂函数的操作让定义路由变复杂了。在单脚本程序中,程序实例存在于全局作用域中,路由可以直接使用 app.route 修饰器定义。但现在程序在运行时创建,只有调用 create_app() 之后才能使用 app.route 修饰器,这时定义路由就太晚了。
    Flask 提供了 Blueprint (蓝本) 的功能,让我们可以实现模块化的应用。使
    用它主要有以下好处:
    1. 将一个复杂的大型应用分解成若干蓝本的集合,也就是若干个子应用或者说模
    2. 块,每个蓝本都包含了可以作为独立模块的视图、模板和静态文件等;
      制作通用的组件,使开发者更易复用组件;
#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() 中注册到程序上,代码看工程方法那。

视图函数中使用

#app/main/errors.py:蓝本中的错误处理程序

from flask import render_template
from . import main

@main.app_errorhandler(404) #注册程序全局的错误处理程序,必须使用app_errorhandler
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
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')) #路由的端点名加上一个命名空间,命名空间就是蓝本的名字( Blueprint 构造函数的第一个参数)
    return render_template('index.html', form=form, name=session.get('name'), known=session.get('known', False), current_time=datetime.utcnow())

启动脚本:

顶级文件夹中的 manage.py 文件用于启动程序。

manage.py:启动脚本
#!/usr/bin/env python
import os
from app import create_app, db
from app.models import User, Role
from flask.ext.script import Manager, Shell
from flask.ext.migrate import Migrate, MigrateCommand

app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)

def make_shell_context():
    return dict(app=app, db=db, User=User, Role=Role)

manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)

if __name__ == '__main__':
    manager.run()

需求文件:

程序中必须包含一个 requirements.txt 文件,用于记录所有依赖包及其精确的版本号。如果要在另一台电脑上重新生成虚拟环境,这个文件的重要性就体现出来了,例如部署程序时使用的电脑。 pip 可以使用如下命令自动生成这个文件:

(venv) $ pip freeze >requirements.txt

如果你要创建这个虚拟环境的完全副本,可以创建一个新的虚拟环境,并在其上运行以下命令:

(venv) $ pip install -r requirements.txt

单元测试:

使用 Python 标准库中的 unittest 包编写。 setUp() 和 tearDown() 方法分别在各测试前后运行,并且名字以 test_ 开头的函数都作为测试执行。
常用assert断言语句

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值