Flask(7)-大型程序的结构

时间:2025-02-23 09:10:00

项目结构

|-flasky
  |-app/         #所有flask程序
    |-templates/ #模板
    |-static/    #css文件
    |-  #数据库模型
    |-   #email
    |-__init__.py #程序包的构造函数,工厂函数在其中定义
    |-main/
      |-__init__.py
      |-
      |-


  |-migrations/
  |-tests/
  |-venv/
  |-
  |-
  |-

四个*文件夹:

  • Flask程序一般保存在名为app的包中;
  • migrations文件夹包含数据库迁移脚本;
  • 单元测试编写在tests包中;
  • venv包含虚拟环境。

同时还创建一些新文件:

  • 列出所有依赖包;
  • 存储配置;
  • 用于启动程序以及其他的程序任务。

配置选项

: 程序的配置

import os
basedir = os.(os.(__file__))
class Config:
    SECRET_KEY = os.('SECRET_KEY') or 'hard to guess string'
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    FLASKY_MAIL_SUBJECT_PREFIX = '[FLASKY]'
    FLASKY_MAIL_SENDER = 'Flasy Admin <flasky@>' 
    FLASKY_ADMIN = os.('FLASKY_ADMIN')

    @staticmethod
    def init_app(app)
        pass

class DevelopmentConfig(Config):
    DEBUG = True
    MAIL_SERVER = ''
    MAIL_PORT = 587
    MAIL_USE_TLS = True
    MAIL_USERNAME = os.('MAIL_USERNAME')
    MAIL_PASSWORD = os.('MAIL_PASSWORD')
    SQLALCHEMY_DATABASE_URI = os.('DEV_DATABASE_RUL') or \'sqlite:///' + os.(basedir, '')

class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.('TEST_DATABASE_RUL') or \'sqlite:///' + os.(basedir, '')

class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.('DATABASE_RUL') or \'sqlite:///' + os.(basedir, '')

config = {
    'development': DevelopmentConfig
    'testing': TestingConfig
    'production': ProductionConfig

    'default': DevelopmentConfig
}

程序包

程序包包含模板、代码、静态文件。
templates和static文件被保存在app中,数据库模型和电子邮件支持分别被保存为app/和app/。

使用程序工厂函数

单个文件中开发程序有个问题,因为程序在全局作用域中创建,因此无法动态修改配置。
解决这个问题的方法是延迟创建程序实例,把创建实例的过程移到可显示调用的工厂函数中。这种方法可以给脚本留出配置程序的时间,还可以创建多个程序实例。
示例 app/__init__.py: 程序包的构造文件

from flask import flask, render_template
from  import Bootstrap
from  import Mail
from  import Moment
from  import SQLAlchemy
from config import config
# 导入大多数的扩展

bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
# 尚未初始化所需的程序实例(即无app = Flask(__name__)),所以没有初始化扩展,即创建扩展类时没有传入参数。

def create_app(config_name): #工厂函数,接收程序使用的配置名作为参数
    app = Flask(__name__)     #初始化程序实例
    .form_object(config[config_name]) 
    #通过配置对象的form_object方法直接导入配置
    config[config_name].init_app(app) #初始化配置

    bootstrap.init_app(app) #初始化
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)

    # 附加路由和自定义的错误页面
    return app #工厂函数返回创建的程序实例
  • 什么是配置名、配置对象、配置类

在蓝本中实现程序功能

转换成程序工厂函数的操作让定义路由变得复杂,因为现在程序在运行时创建,只有调用create_app()之后才能使用修饰器,这时定义路由就太晚了。

Flask使用蓝本提供更好方法。蓝本也可以定义路由,但蓝本中定义的路由出于休眠状态,知道蓝本注册到程序上时,路由才真正成为程序的一部分。使用全局作用域中的蓝本和单脚本一样。

和程序一样,蓝本可以在单个文件中定义,也可以使用更结构化的方式在包的多个模块中创建。为获得最大的灵活性,程序包中创建一个子包,用于保存蓝本。
示例 app/main/__init__.py: 创建蓝本

from flask import Blueprint

main = Blueprint('main', __name__) #接收两个参数,蓝本的名字和蓝本所在包或模块的名字

from . import views, errors
  • 程序的路由和错误处理程序分别保存在app/main/模块和app/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/: 蓝本中的错误处理程序
from flask import render_template
from . import main

@main.app_errorhandler(404)
def page_not_found(e):
    return render_template(''), 404

@main.app_errorhandler(500)
def internal_server_error(e):
    return render_template(''), 500

在蓝本中编写错误处理程序稍有不同,如果使用 errorhandler 修饰器,那么只有蓝本中的错误才能触发处理程序。要想注册程序全局的错误处理程序,必须使用 app_errorhandler.

蓝本中定义路由:

示例 app/main/: 蓝本中定义的程序路由
from datetime import datetime
from flask import render_template, session, redirect, url_for

from . import main
from .froms import NameForm
from .. import db
from ..models import User

@('/', methods=['GET', 'POST'])
def index():
    name = NameForm()
    if form.validate_on_submit():
    # ...
    return redirect(url_for('.index'))
return render_template('', 
                        from=from, name=('name'),
                        known=('known', False),
                        current_time=())

蓝本中编写视图函数有两点不同:

  • 和之前错误处理程序一样,路由修饰器由蓝本提供
  • url_for()函数用法不同。url_for()函数的第一个参数是路由的端点名,Flask为蓝本中的所有端点增加一个命名空间(蓝本的名字),所以视图函数的端点名为,在蓝本中可以省略蓝本名。

启动脚本

*文件夹中的文件用于启动程序。

示例 :   启动脚本
#!/user/bin/env python
import os
from app import create_app, db
from  import Role, User
from  import Manager, Shell
from  import Migrate, MigrateCommand

app = create_app(('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__':
    ()

这个脚本先创建程序。

需求文件

程序中必须包含一个文件,用于记录所有依赖包及其精确的版本号。

单元测试

示例 tests/test_basics.py: 单元测试
import unittest
from flask import current_app
from app import create_app, db

class BasicsTestCase():
    def setUp(self):
         = create_app('testing')
        self.app_context = .app_context()
        self.app_context.push()
        db.create_all()

    def tearDown(self):
        ()
        db.drop_all()
        self.app_context.pop()

    def test_app_exists(self):
        (current_app is None)

    def test_app_is_testing(self):
        (current_app.config['TESTING'])