1 引言
ASP站长网Flask作为Python语言web开发的三大顶梁柱框架之一,对于配置的管理当然必不可少。一个应用从开发到测试到最后的产品发布,往往都需要多种不同的配置,例如是否开启调试模式、使用哪个数据库等等,这些配置都可能因开发阶段和环境而异。
2 Flask配置类:Config
为了达到对配置方便快捷而又灵活管理的目的,Flask提供了一个名为“config的”属性,这个属性在Flask应用实例化时创建,所以,只要创建了Flask应用,就可以使用这个config属性进行配置管理。我们先创建一个Flask应用,去看一看这个config属性:
from flask import Flask
app = Flask(__name__)
print(type(app.config))
输出结果:
<class 'flask.config.Config'>
可以看出,app.config是一个类,一个定义在flask.config模块中的类。既然是一个类,我们就可以推测,Flask在实例化应用时,也实例化了这个Config类,我们通过这个类提供的各种属性、方法来进行配置管理。如果你用的IDE是pycharm,按住Ctrl鼠标左键点击app.config中的config就可以定位到Flask类中定义config属性的源码,这一行源码如下:
self.config = self.make_config(instance_relative_config)
在一行代码在Flask构造方法__init__()中,正如刚才所说,确实是在实例化Flask应用时创建了config属性。不过在值是Flask类中的make_config()方法的返回值,参数instance_relative_config是__init__()方法的参数,默认为False,具体功能我们在下文解析时用到再说。现在,我们去看一下make_config()方法的源码:
def make_config(self, instance_relative=False):
root_path = self.root_path # root_path是主模块所在绝对路径
# 下面这个instance_relative就是Flask构造方法里面的instance_relative_config
if instance_relative: # 如果实例化Flask传入的instance_relative_config为True
root_path = self.instance_path # instance_path也是Flask构造方法中的参数,是一个路径,如果实例化Flask时没有为instance_path传参则默认路径为Flask实例同级目录下的instance目录
defaults = dict(self.default_config) # 读取Flask初始化时的默认配置
defaults["ENV"] = get_env() # 判断环境类型:production或development,即生产环境或开发环境,设置这个值是因为有些应用需要根据这个值来改变行为
defaults["DEBUG"] = get_debug_flag() # 是否开启调试模式
return self.config_class(root_path, defaults) # 实例化一个Config类
make_config()方法执行可以分为4个步骤:读取配置文件路径、读取默认配置、设置环境和模式、创建Config配置类。可以说,前面3个步骤都是为创建Config类做准备,里面的细节大家看上面代码注释就明白了,重点在于创建Config类,继续往下查看config_class:
config_class = Config # 将Config赋值给config_class
config_class就是Config类,这里只不过做了一个赋值。继续查看Config:
class Config(dict):
def __init__(self, root_path, defaults=None):
dict.__init__(self, defaults or {})
self.root_path = root_path
当看到这个Config类代码时,仿佛一切都恍然大悟——一切配置操作都在这里。从源码中我们可以看到,Config类继承了dict,也即是说,Config类就是一个字典,一切字典所拥有的使用方法,在Config类上也行得通。
大概浏览config.py文件,可以看到,在Config类中还提供了几个名称很相似的方法:
from_object(self, obj)
from_pyfile(self, filename, silent=False)
from_envvar(self, variable_name, silent=False)
from_json(self, filename, silent=False)
from_mapping(self, *mapping, **kwargs)
阅读方法文档获知,这几个方法是读取配置用的,只不过读取的目标不一样,也就是说,Flask通过提供这几个方法为用户提供了多种配置管理方式。
接下来,我们来捋一捋Flask的配置管理方式。
3 配置方式1:直接赋值
通过上面的分析我们知道,Config类继承类字典类,所以我们可以用字典的方式进行配置管理,例如是config['key'] = value的方式赋值,通过config.get(key)方式取值:
from flask import Flask
app = Flask(__name__)
app.config['DEBUG'] = True
print('是否开始调试模式:', app.config.get('DEBUG'))
输出:
是否开始调试模式: True
注意:Flask中所有配置名称都是大写。上面DEBUG配置中,如果写成了debug,那就会在app.config中添加一个debug的配置,而不是修改DEBUG,开启调试模式就会失败。
也可以调用字典类中的一些方法,例如调用update方法一次性设置多个值:
from flask import Flask
app = Flask(__name__)
app.config.update(
DEBUG=True,
TESTING=True
)
print('debug:', app.config.get('DEBUG'))
print('testing:', app.config.get('TESTING'))
甚至可以将一些默认配置中没有的值存入配置中:
from flask import Flask
app = Flask(__name__)
app.config['aaaaa'] = '我是aaaaa'
print(app.config['aaaaa'])
输出:
我是aaaaa
对于一些小应用来说,这种确实很是简单方便,但是对于更为复杂的应用,可能需要针对不同的环境使用不同的配置,配置的内容又多,这种方法就显得麻烦了。这时候就需要用到Config类中实现的几个方法了。
4 配置方式2-对象中配置:from_object(推荐)
先来看看from_object()方法的源码:
def from_object(self, obj):
if isinstance(obj, string_types): # 判断obj是否是str类型
obj = import_string(obj) # 如果是str类型,就根据这个字符串导入对象
for key in dir(obj): # 遍历obj的所有值
if key.isupper():
self[key] = getattr(obj, key) # self指的就是config实例本身,通过getattr取出对应的值进行
从源码可以看出,from_object()方法说接收的参数obj可以使str类型,可以是一个模块,甚至是一个类。
我们先尝试一下是一个模块的情况,创建一个settings.py模块,内容如下:
DEBUG = False
TESTING = False
这里只写了两个配置,你可以写更多,无所谓。怎么使用呢?
from flask import Flask
import settings
app = Flask(__name__)
app.config.from_object(settings)
print('DEBUG:', app.config.get('DEBUG'))
print('TESTING:', app.config.get('TESTING'))
print('A:', app.config.get('A'))
输出:
DEBUG: True
TESTING: True
A: 123
当obj是一个字符串时:
from flask import Flask
app = Flask(__name__)
app.config.from_object('settings')
print('DEBUG:', app.config.get('DEBUG'))
print('TESTING:', app.config.get('TESTING'))
print('A:', app.config.get('A'))
输出:
DEBUG: True
TESTING: True
A: 123
看出来了吗?无论是使用app.config.from_object(settings)还是app.config.from_object('settings')使用的都是使用settings.py文件中的配置,至于原因,如果不明白就回去看看上面的源码。
如果obj是一个类时,我们修改一下settings.py,如下:
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
在settings.py模块中,我们定义了多个类,首先是Config类,这个类定义的是默认配置,其他类都继承Config类,每一个之类代表一种配置,如果需要子类中可以覆写Config,如果不覆写则使用Config中的默认配置。怎么使用呢?
from flask import Flask
import settings
app = Flask(__name__)
app.config.from_object(settings.ProductionConfig)
print('DEBUG:', app.config.get('DEBUG'))
print('TESTING:', app.config.get('TESTING'))
print('DATABASE_URI:', app.config.get('DATABASE_URI'))
输出:
DEBUG: False
TESTING: False
DATABASE_URI: mysql://user@localhost/foo
使用这种方法的好处是可以充分利用面向对象中继承等的优良特性共享配置,设置多套配置,使用时,只需要针对实际需要修改app.config.from_object(settings.ProductionConfig)中传入的类即可。这种方法在实际开发中也是使用最多的。