一:问题的引出
最近正在读flask web开发这本书,读到第七章, 因为在单一脚本中编写小型web程序很方便, 但是程序变复杂后, 使用单个大型源码文件会很不方便, 于是我们使用包和模块来组织大型程序。
我们之前的hello.py如下图所示:
不难看出程序实例app是先配置config变量,然后再用其初始化Manager, Bootstrap等拓展。
但是当我们改用包和模块来组织大型程序后,用python manage.py runserver 代替了原来的 python hello.py runserver 运行服务器,我们来看一下manage.py的内容:
发现其用了app/__inti__.py里面的create_app来创建程序实例,然后就直接初始化剩余的两个拓展了, 所以我们猜测create_app完成了config变量的配置, 和大部分拓展的初始化,我们来查看一下create_app这个函数:
我们发现 create_app先创建了app这个实例,然后调用代码,app.config.from_object(config[config_name]),然后对拓展进行初始化,我们猜测 ,这行代码的作用应该就是配置config变量,但是它是如何实现的呢? 下面我们来讨论这个问题:
一.我们首先从程序实例的创建开始谈起: app = Flask(__name__)
1. Flask的构造函数里面调用了其基类_PackageBoundObject的构造函数
2.查看基类构造函数
(1)Import_name就是Flask(__name__)中的__name__参数,也就是模块的名字
(2)template_folder也就是静态文件static
(3)下面来看root_path= get_root_path(self.import_name)
3.找get_root_path
发现这个函数是返回模块所在包的路径。从前面可知,import_name的值就是Flask(__name__)中的__name__参数,__name__参数代表app包中的__init__.py模块,所以这个函数返回的也就是app包的路径。即root_path就是app包的路径。(注意,此处的app不是程序实例,是包名)
二.Falsk构造函数往下面看, 发现实例属性config:
1.找instance_relative_config
发现instance_relative_config是构造实例时传入的关键字参数,默认为Flase
2.在flask类里找到self.make_config
(1)self.root_path.
前面我们知道root_path存的是模块所在包app的路径。
(2)先找self.default_config
发现default_config是flask类的一个属性,ImmutableDict:不可变字典(?)
(3).找config_class
发现config_class是flask类的一个属性,其值Config是从config.py中导入的一个类
所以make_config函数里面的最后一句return self.config_class(root_path, self.default_config)就等价于:
return Config(root_path, self.default_config);
不难看出,self.config就是Config类的一个实例。
(4).Config类的构造函数
.
调用了dict的构造方法__init__,所以实例本身就是由defaults参数,也就是default_config构造的一个字典,而且实例有一个属性root_path,前面有介绍root_path就是模块所在包的路径。
经过以上分析我们可以知道,当我们调用app = Flask(__name__)时, 创建了Flask类的实例app, 并为其指定了一系列的属性, 其中的config属性是Config类的一个实例, 其本身是由实例的另一个属性self.default_config(上文可见)作为参数构造的一个字典。
三. 还记得我们在app/__init__.py里面的调用app.config.from_object(config[config_name])吗?
(1)config[config_name]
config是config.py里面的字典:
config_name是’default’
所以app.config.from_object(config[config_name])就等价于app.config.from_object(DevelopmentConfig)
(1) DevelopmentConfig就是Config类的继承类,下面我们贴出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
SQLALCHEMY_TRACK_MODIFICATIONS= False
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')
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
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
}
DevelopmentConfig有两个类属性,并且继承了其基类的属性和方法
(2)from_data
不难看出Config实例app.config的方法from_object肯定是在Config类里面:
app.config.from_object(DevelopmentConfig),obj参数就是DevelopmentConfig
所以for key in dir(obj):遍历到的就是Development里所有的属性和其继承到的属性:
然后if判断只要key是大写的就把key作为键,把key对应的值作为值,添加导self这个字典中,还记得self是什么的,没错就是app.config!
由此,我们可以的出结论:
app.config.from_object(config[config_name])的作用就是配置所有的config变量。