应用配置

1. 加载配置

  • app.config 用于设置配置, 该属性继承自 dict, 可以以字典形式赋值取值

代码示例

from datetime import timedelta
from flask import Flask, session

app = Flask(__name__)
app.secret_key = 'test'

# config属性用于设置配置
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)

@app.route('/')
def index():
    # 设置session 用于测试配置是否生效
    session['name'] = 'zs'

    # 读取配置
    print(app.config.get('PERMANENT_SESSION_LIFETIME'))  
    return "index"


if __name__ == '__main__':
    app.run(debug=True)
  • 实际开发中, 应该将配置封装起来, 方便进行统一的管理, 类似Django的settings文件
  • Flask提供了多种封装方式, 这里先介绍一种常用的方案 -- 从对象中加载配置

从对象中加载配置

实际开发中, 项目往往存在多套配置

  • 开发环境配置
  • 生产环境配置
  • 测试环境配置
  • ...
  • 各种环境的配置既有相同也有不同, 大量相同的配置会出现代码冗余
  • 从对象中加载配置
    • 配置封装方案之一, 以面向对象的形式 封装配置, 有利于 减少重复代码 以及 代码解耦合

代码示例

  • 定义配置文件 config.py, 在文件中将应用配置 以类的形式 封装起来
# config.py 

from datetime import timedelta


class BaseConfig:
    """配置基类  可以将相同的配置抽取到基类中, 减少重复代码"""

    # 定义和配置同名的类属性
    PERMANENT_SESSION_LIFETIME = timedelta(days=7)


class DevelopmentConfig(BaseConfig):
    """开发环境"""
    SQL_URL = '127.0.0.1:3306/test1'  # 数据库地址


class ProductionConfig(BaseConfig):
    """生产环境"""
    SQL_URL = '222.10.15:3306/users'  # 数据库地址
  • 主文件 main.py 从对象中加载封装的配置 app.config.from_object()
# main.py 

from datetime import timedelta
from flask import Flask

app = Flask(__name__)

# 从对象中加载配置
# 优点: 面向对象的设计有利于 减少重复代码 以及 代码解耦合
from config import DevelopmentConfig
app.config.from_object(DevelopmentConfig)


@app.route('/')
def index():
    print(app.config.get('PERMANENT_SESSION_LIFETIME'))
    return "index"


if __name__ == '__main__':
    app.run(debug=True)

2. 切换配置

  • 虽然封装了多套配置, 但需要 修改代码才能切换配置, 这种方式并不利于开发和测试
  • Flask提供了切换配置的更好方案, 需要进行以下两步:
    • 定义工厂函数, 封装应用的创建过程
    • 利用环境变量, 调用工厂函数, 指定配置并动态创建应用

2.1 定义工厂函数

  • 定义工厂函数, 封装应用的创建过程

代码示例

  • 在前一节 config.py 的基础上, 定义字典记录配置类型和配置子类之间的映射关系
# config.py 

from datetime import timedelta


class BaseConfig:
    """配置基类  可以将相同的配置抽取到基类中, 减少重复代码"""

    # 定义和配置同名的类属性
    PERMANENT_SESSION_LIFETIME = timedelta(days=7)


class DevelopmentConfig(BaseConfig):
    """开发环境"""
    SQL_URL = '127.0.0.1:3306/test1'  # 数据库地址


class ProductionConfig(BaseConfig):
    """生产环境"""
    SQL_URL = '222.10.15:3306/users'  # 数据库地址


# 定义字典来记录 配置类型 和 配置子类  之间的映射关系
config_dict = {
    'dev': DevelopmentConfig,
    'pro': ProductionConfig
}
  • 在主文件 main.py 中, 定义工厂函数封装应用的创建过程, 并通过参数指定应用对应的配置类型
from flask import Flask, current_app, Config
from config import config_dict


# 工厂函数: 根据参数需求, 内部封装对象的创建过程
def create_app(config_type):
    """封装应用的创建过程"""

    # 创建应用
    flask_app = Flask(__name__)

    # 根据配置类型取出对应的配置子类
    config_class = config_dict[config_type]

    # 加载普通配置
    flask_app.config.from_object(config_class)

    return flask_app


# 创建应用对象
app = create_app('dev')


@app.route("/")
def index():
    print(app.config.get('SQL_URL'))
    return "index"


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

2.2 动态创建应用

  • 通过代码调用函数 create_app('dev') 创建应用, 仍然在代码中写死了 配置类型dev
  • FLASK提供的环境变量 FLASK_APP 允许 以调用函数的形式动态创建应用
    • 格式: FLASK_APP="应用所在模块:工厂函数(参数)"
$ export FLASK_APP="main:create_app('dev')"  # 设置环境变量, 通过调用函数 设置配置并动态创建Flask应用
$ flask run  # 运行web程序

注意: 如果动态创建应用, 通过 app = create_app('dev') 代码生成的Flask应用将会失效

  • 需要使用 app 的时候, 可以使用 current_app来代替 (只能在请求范围中使用)
  • 将无法通过 app.route()来定义路由, 但是 蓝图定义路由仍然有效

代码示例

from flask import Flask, current_app
from config import config_dict


def create_app(config_type):
    """封装应用的创建过程"""

    flask_app = Flask(__name__)
    config_class = config_dict[config_type]
    flask_app.config.from_object(config_class)

    # 由于动态创建应用(app失效), 不能通过app.route定义路由 
    # 添加路由规则  (实际开发中使用较少, 以蓝图定义路由为主)
    flask_app.add_url_rule('/', index.__name__, index)

    # 注册蓝图对象  蓝图可以正常使用
    # from home import home_blu
    # flask_app.register_blueprint(home_blu)

    return flask_app


# 定义视图函数, 不添加app.route()装饰器, 通过 添加路由规则 的方式手动定义路由
def index():  
    print(current_app.config.get('SQL_URL'))

    return "index"


# app = create_app('dev')  # app无效


# @app.route("/")  # app无效  
# def index():
#     print(app.config.get('SQL_URL'))
#     return "index"


# if __name__ == '__main__':
#     app.run()  # app失效

3. 加载隐私配置

  • 对于线上项目, 隐私配置 直接写在项目的配置文件中, 会 增加数据泄露的风险
  • FLASK提供了 从环境变量中加载配置 的方案, 可用于加载隐私配置
    • 该方案允许 通过环境变量指定配置文件的路径, 且 路径允许在项目目录以外
    • 环境变量格式: export 环境变量名="隐私配置的文件路径"

代码示例

  • 在项目目录外 创建隐私配置文件secret_config.py, 并以全局变量形式设置隐私配置
# secret_config.py

SECRET_KEY = 'heima123'   # 隐私配置
  • 主文件main.py 通过环境变量方式来加载隐私配置
# main.py

from datetime import timedelta
from flask import Flask

app = Flask(__name__)

# 从环境变量中加载配置
# 优点: 可以保护隐私配置   export ENV_CONFIG="隐私配置的文件路径"
app.config.from_envvar('ENV_CONFIG')


@app.route('/')
def index():
    print(app.config.get('SECRET_KEY'))
    return "index"


# if __name__ == '__main__':
#     app.run(debug=True)
  • 环境变量&终端命令 启动程序
$ export FLASK_APP="main"  # 设置内置环境变量
$ export ENV_CONFIG="/xx/secret_config.py"  # 设置隐私配置对应的环境变量
$ flask run  # 启动web程序

实际开发中的方案

  • 开发阶段, 只加载普通配置
  • 生产阶段, 先加载普通配置, 再通过环境变量的方式加载项目以外的隐私配置并覆盖原有配置

代码示例

def create_app(config_type):
    """封装应用的创建过程"""

    # 创建应用
    flask_app = Flask(__name__)
    # 根据配置类型取出对应的配置子类
    config_class = config_dict[config_type]

    # 先加载普通配置
    flask_app.config.from_object(config_class)
    # 再加载隐私配置  silent=True, 配置加载失败也不报错
    flask_app.config.from_envvar('ENV_CONFIG', silent=True)

    return flask_app
  • 加载配置时, 设置参数 silent=True, 则配置加载失败也不会报错
posted @ 2021-02-07 16:11  Tracydzf  阅读(186)  评论(0)    收藏  举报