Flask蓝本(Blueprint)

一、动态配置Flask app

在使用单个文件开发Flask应用程序时,由于Flask对象app是在全局作用域中创建的,在运行这个全局文件脚本时,app就已经创建,所以此时修改app的配置是不会生效的。为了解决这个问题可以把app的创建移到可显式调用的工厂函数中,即延迟创建app实例。

工厂函数:app/__init__.py

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect

from config import config

db = SQLAlchemy()
csrf = CSRFProtect()


# 创建Flask实例,注册第三方扩展,调用config.init_app初始化
def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    db.init_app(app)   # 注册第三方扩展
    csrf.init_app(app)

    return app  # 返回app实例

此时调用create_app()即可动态传入config_name参数实现动态配置。

二、使用蓝本

使用工厂函数创建app实例可以解决了动态配置的问题,但是程序路由应该如何配置呢?我们在使用单个文件开发Flask程序时,在全局作用域创建了app实例,直接使用app.route就可配置路由,而现在app实例的创建已经移到工厂函数中进行了,只有调用工厂函数create_app后才能使用app实例,然而我们的路由函数在编写程序时已经定义好了,也就是说在编写路由函数时是无法获取到app实例的,也就无法定义路由配置。通用的所有需要在编码时使用app实例的配置此时都无法使用。

为了解决这个问题flask提供了蓝本。

蓝本:app/main/__init__.py

from flask import Blueprint

main = Blueprint('main', __name__)  # 创建蓝本

from . import  views   # 在views定义程序的路由

在views中定义的程序路由只有在蓝本注册到app实例上后才有效。

# 创建Flask实例,注册第三方扩展,调用config.init_app初始化
def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    db.init_app(app)
    csrf.init_app(app)

    from .main import main as main_blueprint  # 导入蓝本
    app.register_blueprint(main_blueprint)  # 注册蓝本

    return app

完整的(app/__init__.py)

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_wtf.csrf import CSRFProtect

from config import config

db = SQLAlchemy()
csrf = CSRFProtect()


# 创建Flask实例,注册第三方扩展,调用config.init_app初始化
def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    db.init_app(app)
    csrf.init_app(app)

    #注册蓝本
    from .main import main as main_blueprint
    from .auth import auth as auth_blueprint
    app.register_blueprint(main_blueprint)
    app.register_blueprint(auth_blueprint, url_prefix = '/auth')

    return app

views.py

from datetime import datetime

from flask import render_template, session, redirect, url_for

from . import main # 导入蓝本


@main.route('/home', methods = ['GET', 'POST']) # 定义路由
def index():
    return render_template('welcome.html')

三、定义程序启动脚本

manage.py

import os


from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand

from app import create_app
from app import db

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)


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

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

在定义程序脚本时需要使用Flask-Script和Flask-Migrate包。运行python manage.py即可启动程序。

四、完整的项目结构

|-- myproject
    |-- config.py
    |-- flask-env.yml
    |-- manage.py
    |-- app
    |   |-- models.py
    |   |-- __init__.py   # 创建app实例的工厂函数
    |   |-- auth  # auth蓝本
    |   |   |-- forms.py
    |   |   |-- views.py
    |   |   |-- __init__.py
    |   |   |-- templates
    |   |-- main   # main蓝本
    |   |   |-- errors.py
    |   |   |-- forms.py
    |   |   |-- views.py
    |   |   |-- __init__.py
    |   |-- static
    |   |   |-- css
    |   |   |-- image
    |   |   |-- js
    |   |-- templates
    |-- migrations
    |-- tests
    |   |-- __init__.py
    |-- venv

五、动态配置类

config.py


import os
basedir = os.path.abspath(os.path.dirname(__file__))


# 通用配置
class Config:
    WTF_CSRF_SECRET_KEY = os.environ.get('WTF_CSRF_SECRET_KEY') or 'ao blog csrf key'
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'ao blog session key'
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    # 初始化Flask实例,对当前环境的配置初始化
    @staticmethod
    def init_app(app):
        app.config['SECRET_KEY'] = SQLALCHEMY_COMMIT_ON_TEARDOWN
        app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = SQLALCHEMY_COMMIT_ON_TEARDOWN
        app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = SQLALCHEMY_COMMIT_ON_TEARDOWN
        app.config['WTF_CSRF_SECRET_KEY'] = SQLALCHEMY_COMMIT_ON_TEARDOWN


# 开发环境配置
class DevelopmentConfig(Config):
    DEBUG = True
    # SQLALCHEMY_TRACK_MODIF = True
    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('DEV_DATABASE_URL') or \
                              'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')

class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
                              'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')

config = {
    'dev': DevelopmentConfig,
    'test': TestingConfig,
    'prod': ProductionConfig,
    'default': DevelopmentConfig
}

posted @ 2020-04-11 23:38  我爱这世间美貌女子  阅读(511)  评论(0编辑  收藏  举报