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配置部分遇到了问题,然后,遇见了下面的好文:
原来,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
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
后记
感觉学习Flask走上正轨了!
可以编写run程序了,接下来就是部署了:
-使用Apache+mod_wsgi运行Flask应用
-使用Nginx+uWSGI运行Flask应用
-使用Tornado运行Flask应用
-使用Gunicorn运行Flask应用
好多啊!来自Flask进阶系列(八)–部署和分发 by Billy.J.Hee的技术博客,昨晚发现,超级好的!
当然,不懂得、不清楚的还不少,都需要练习啊!
之前五月看过一遍Flask的官文了,以为自己OK呢,结果,才做这么个小小的项目就遇到这么多问题!真是……
做项目!做项目!做项目!
真的很重要的!也是学习东西最好的方式!
今天的任务列表中,最最重要的就是继续昨天的项目了!
加油!
Flask官文配置参考链接: