Flask:文件配置方式实践及其中的各种问题记录

Windows 10家庭中文版,Python 3.6.4,Flask 1.0.2,

 

提示:

1.请查看本文后面的“18-07-17  11:18重大纠正 !

2.flask run命令运行时传入参数给create_app的方法也有了,参考后面的18-07-18 12:47更新

3.请查看18-07-18 13:16更新:instance_relative_config=True,很重要!原来孤创建Flask应用时使用了这个参数,所以参会有一些列instance目录的问题!

 

----正文----

 

昨日创建了一个Flask应用,一直都是使用flask run命令来执行,期间在Flask配置部分遇到了问题,然后,遇见了下面的好文:

Python flask中的配置

原来,Flask项目下可以有个config.py文件或者config包——其中包含各种环境(本地、生产、其它)的默认配置文件,然后使用app.config.from_object()函数从这些默认配置中获取配置来启动Flask应用——需要条件判断,这个条件判断的值来自create_app或make_app工厂函数的参数

在当前的Flask中,create_app或make_app的参数默认类型为ScriptInfo,不是很了解;不过,也可以传入字典或其它,可以不只一个参数,由开发者根据需要自行决定。

除了默认的config.py文件或者config包外,开发者还可以在instance目录(自己还不是特别熟悉)下放置敏感级别高的配置文件,比如,数据库密码。

 

在使用flask run命令运行应用时,instance目录位于项目的根目录下,可以使用app.config.from_pyfile('config.py')函数来获取配置启动Flask应用,这也是官文Application Factories中使用def create_app(config_filename)、app.config.from_pyfile(config_filename)的原因。前面一直用flask run命令运行,所以,使用print获取的信息显示config_filename为ScriptInfo类型,但没有配置文件——之前自己没有建立instance目录。

 

现在,进入新阶段吧!使用程序调用create_app(...)函数来创建Flask应用并运行!在上面的参考好文中以及其它一些资料中,都是这么运行Flask应用的,恐怕全世界只有自己没有这么玩过了!不过,现在这个状况已经成为过去了!

 

下面是自己的create_app的代码(最终版本):

 1 # started from 2018-07-16
 2 from flask import Flask
 3 
 4 def create_app(config = None):
 5     app = Flask(__name__, instance_relative_config=True)
 6     
 7     # 根据传入的config决定使用哪个默认配置
 8     # 在flask run时执行错误!此时config的类型为ScriptInfo,是不存在get()函数的
 9     # 目前传入为字典类型,后面可以更改为ScriptInfo类型,官文说它未来有很大用处
10     # config还可以为None
11     app_config = 'default'
12     
13     if isinstance(config, dict):
14         app_config = config.get('config')
15         
16         if app_config not in ['default', 'deploy']:
17             print('错误:不存在的配置:', app_config)
18             print('提示:重置为默认配置启动')
19             app_config = 'default'
20     
21     app.config.from_object('config.' + app_config)
22     
23     # 来自instance目录的配置文件
24     app.config.from_pyfile('config.py')
25     
26     from . import mdb, news
27     
28     mdb.init_app(app)
29     
30     app.register_blueprint(news.bpnews)
31     
32     return app

 

建立程序使用create_app(...)创建Flask应用的代码(runApp.py):

1 # runApp.py for webnews project
2 from webnews import create_app
3 
4 # config: default or deploy
5 app = create_app({'config':'defaul1t'})
6 app.run()

 

说明,runSpider.py位于项目webnews的根目录——因为项目尚未安装、整个项目在virtualenv中运行。

 

在runApp.py中,导入了create_app,并使用它创建Flask应用,传入参数为一个简单的字典;

因为这里传入的是字典,所以,在create_app()函数运行后接收到的也是字典(而不是默认的ScriptInfo,当然,也可以在程序中创建ScriptInfo对象来传入配置数据,或许和instance的位置有关系),函数中的操作也主要是对字典类型的config的类型判断和数据读取。

在上面的程序定义下,create_app只可以传入一个参数,可以是字典,也可以为None,也可以为其它任何类型,但是,程序中只对字典类型进行了判断并处理,如果不是字典,那么,忽略,并使用默认配置启动。

 

上面的程序既可以使用flask run命令来启动,也可以使用类似runApp.py的程序来启动——这也是为什么之前说这种工程函数方式可以启动多个Flask应用的原因所在。

 

注意,使用上面的runApp.py运行Flask应用时,instance目录不再是项目下的instance目录了。因为程序运行中virtualenv中,此时,instance目录为虚拟环境根目录下的var\\webnews-instance——或许可以配置为其它的,var有点像Linux下的目录名,后面会看到这个错误提示的截图。

 

下面展示一些完善过程中的调试过程:

-最开始运行,没有找到instance目录

-按照错误提示建立instance目录

-instance目录建立后,运行成功

 

-DEBUG = True:开启调试模式

 

-以为config是ScriptInfo类型,所以调用config.data来获取数据,结果失败了!

 

-获取传入的配置字典参数

-获取成功,并使用默认配置运行(项目的config目录下default.py文件,还有一个deploy目录)

 

-传入错误的配置参数,运行失败

-修改代码后——添加判断和提示,运行成功

 

-flash run运行失败:这种方式运行时没有传入配置参数(怎么传入呢?),此时config为None

 

-完善后的程序(近期的最终版本,不考虑传入参数为ScriptInfo类型)

 

发现一个问题:即便我的默认配置设置了DEBUG为True,但是,在flask run命令运行时却无法开启——设置环境变量FLASK_ENV为development当然是可以的了。

 

18-07-17  11:18重大纠正:

弄错了!

前面使用传入的参数config来使用项目根目录下config里面的配置文件,其实,应该做的是根据config来决定使用instance目录中的配置文件

看来孤的代码要修改了啊!

 

18-07-17  12:29代码更新:

 1 # runApp.py
 2 from webnews import create_app
 3 
 4 # 参数为instance目录下的配置文件名
 5 # 目前可选:dev.py,prod.py,后续可以自行添加
 6 # dev.py、prod.py位于instance目录中,可以使用app.config.root_path查看具体在哪里。
 7 app = create_app({'config_fn':'dev.py'})
 8 
 9 app.run()
10 
11 # create_app(...)
12 from flask import Flask
13 
14 def create_app(config_fn = None):
15     app = Flask(__name__, instance_relative_config=True)
16     
17     # 从config包下的default.py文件获取配置信息
18     app.config.from_object('config.default')
19     
20     # print('app.config.root_path:', app.config.root_path) # debug
21     
22     #
23     # 根据传入的config决定使用哪个配置:来自instance目录(app.config.root_path)
24     # 在flask run时执行错误!此时config的类型为ScriptInfo,是不存在get()函数的
25     # 目前传入为字典类型,后面可以更改为ScriptInfo类型,官文说它未来有很大用处
26     # config还可以为None
27     if isinstance(config_fn, dict):
28         # 获取配置中的参数
29         instance_config = config_fn.get('config_fn')
30         
31         # 获取成功!
32         # 但不确定是否为文件名、文件是否有效,将使用app.config.from_pyfile(instance_config)配置应用
33         # 需要做异常判断
34         #
35         # 非None、str类型判断(是否可以为bytes?)
36         if instance_config and isinstance(instance_config, str):
37             try:
38                 app.config.from_pyfile(instance_config)
39             except FileNotFoundError as e:
40                 print('错误:参数config_fn:', config_fn)
41                 print('错误:配置文件未找到!')
42                 print(e)
43             except Exception as e:
44                 # 输出错误信息
45                 print('错误参数config_fn:', config_fn)
46                 print('错误:其它错误!')
47                 print(e)
48             else:
49                 # 配置成功,输出提示消息
50                 print('消息:使用%s配置app!' % instance_config)
51     else:
52         # flask run命令执行时config_fn一般为None,不做处理
53         # flask run命令执行时,怎么判断并使用项目的instance目录下的配置文件呢?
54         print('警告:参数config_fn的类型(%s)并非字典!' % type(config_fn))
55         print('警告:将使用默认配置文件dev.py进行配置!')
56         # 使用项目根目录下的配置文件dev.py进行配置
57         # 需要确保此文件存在,否则,数据库访问会不成功,
58         # 找到动态配置的方法再改进
59         # 程序中执行时也会传送空参数
60         try:
61             app.config.from_pyfile('dev.py')
62         except Exception as e:
63             print('错误:默认配置dev.py不存在,无法使用其完成配置!')
64         else:
65             print('警告:使用默认dev.py配置成功!')
66     
67     # 现在,亟需知道flask run传入参数的方式!
68     # 环境变量?WEBNEWS_CONF_FILE?
69     # app.config.from_envvar(‘APP_CONFIG_FILE’)将加载由环境变量APP_CONFIG_FILE指定的文件。
70     # 这个环境变量的值应该是一个配置文件的绝对路径。
71     # TBD
72     
73     # 应用扩展、蓝图配置等
74     from . import mdb, news
75     
76     mdb.init_app(app)
77     
78     app.register_blueprint(news.bpnews)
79     
80     return app
webnews创建应用代码

 

18-07-18 12:47更新:

终于找到flask run命令运行时如何给create_app传递参数的方法了——官文Command Line Interface

 

测试:set FLASK_APP=webnews:create_app({"config_fn":"dev.py"}),成功!就是太长了点!这也难怪官文传递的参数为config_filename!懂了!

 

18-07-18 13:16更新:instance_relative_config=True

请注意我上面代码中创建app的参数instance_relative_config=True,这表明使用instance目录,如果设置为False,则不适用,会影响创建的app的app.config.root_path的值。

在上面的代码中,若是设置为False,则app.config.root_path一律为项目下的目录:此时程序运行和flask run运行的结果相同,默认为False。显然,这样的默认值是不试用的。

 

再次更新后的代码:

 1 from flask import Flask
 2 
 3 def create_app(config_fn = None):
 4     '''
 5     创建Flask应用,并使用用户提供的instance目录下的配置文件配置应用
 6     参数config_fn格式:{'config_fn':'filename.py'}
 7     注意:请确定配置文件存在,否则,将不会发生配置
 8     注意:请确定运行时的instance目录是否存在
 9     '''
10     # 注意:instance_relative_config这个参数很重要!关系到是否使用instance目录!
11     app = Flask(__name__, instance_relative_config=True)
12     
13     print('app.config.root_path = ', app.config.root_path) # debug
14     
15     # 从config包下的default.py文件获取配置信息
16     app.config.from_object('config.default')
17     
18     # 根据传入的config决定使用哪个配置:来自instance目录(app.config.root_path)
19     # 目前使用字典方式传入配置文件名:
20     # {'config_fn':'dev.py'}
21     if isinstance(config_fn, dict):
22         # 获取配置中的参数
23         instance_config = config_fn.get('config_fn')
24         
25         # 获取成功!
26         # 但不确定是否为文件名、文件是否有效,将使用app.config.from_pyfile(instance_config)配置应用
27         # 需要做异常判断
28         #
29         # 非None、str类型判断(是否可以为bytes?)
30         if instance_config and isinstance(instance_config, str):
31             try:
32                 # 使用配置文件配置应用
33                 app.config.from_pyfile(instance_config)
34             except FileNotFoundError as e:
35                 print('错误:参数config_fn:', config_fn)
36                 print('错误:配置文件未找到!')
37                 print(e)
38             except Exception as e:
39                 # 输出错误信息
40                 print('错误参数config_fn:', config_fn)
41                 print('错误:其它错误!')
42                 print(e)
43             else:
44                 # 配置成功,输出提示消息
45                 print('消息:使用%s配置app!' % instance_config)
46     else:
47         # 参数不符合本函数定义的规则
48         print('警告:参数config_fn的类型(%s)并非字典!' % type(config_fn))
49     
50     # 应用扩展、蓝图配置等
51     from . import mdb, news
52     
53     mdb.init_app(app)
54     
55     app.register_blueprint(news.bpnews)
56     
57     return app
create_app

 

后记

感觉学习Flask走上正轨了!

可以编写run程序了,接下来就是部署了:

-使用Apache+mod_wsgi运行Flask应用

-使用Nginx+uWSGI运行Flask应用

-使用Tornado运行Flask应用

-使用Gunicorn运行Flask应用

好多啊!来自Flask进阶系列(八)–部署和分发 by Billy.J.Hee的技术博客,昨晚发现,超级好的!

当然,不懂得、不清楚的还不少,都需要练习啊!

之前五月看过一遍Flask的官文了,以为自己OK呢,结果,才做这么个小小的项目就遇到这么多问题!真是……

做项目!做项目!做项目!

真的很重要的!也是学习东西最好的方式!

今天的任务列表中,最最重要的就是继续昨天的项目了!

加油!

 

Flask官文配置参考链接:

Configuration Handling

API Configuration

 

posted @ 2018-07-17 10:07  快乐的凡人721  阅读(1156)  评论(0编辑  收藏  举报