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的代码(最终版本):
# started from 2018-07-16
from flask import Flask def create_app(config = None):
app = Flask(__name__, instance_relative_config=True) # 根据传入的config决定使用哪个默认配置
# 在flask run时执行错误!此时config的类型为ScriptInfo,是不存在get()函数的
# 目前传入为字典类型,后面可以更改为ScriptInfo类型,官文说它未来有很大用处
# config还可以为None
app_config = 'default' if isinstance(config, dict):
app_config = config.get('config') if app_config not in ['default', 'deploy']:
print('错误:不存在的配置:', app_config)
print('提示:重置为默认配置启动')
app_config = 'default' app.config.from_object('config.' + app_config) # 来自instance目录的配置文件
app.config.from_pyfile('config.py') from . import mdb, news mdb.init_app(app) app.register_blueprint(news.bpnews) return app
建立程序使用create_app(...)创建Flask应用的代码(runApp.py):
# runApp.py for webnews project
from webnews import create_app # config: default or deploy
app = create_app({'config':'defaul1t'})
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代码更新:
# runApp.py
from webnews import create_app # 参数为instance目录下的配置文件名
# 目前可选:dev.py,prod.py,后续可以自行添加
# dev.py、prod.py位于instance目录中,可以使用app.config.root_path查看具体在哪里。
app = create_app({'config_fn':'dev.py'}) app.run() # create_app(...)
from flask import Flask def create_app(config_fn = None):
app = Flask(__name__, instance_relative_config=True) # 从config包下的default.py文件获取配置信息
app.config.from_object('config.default') # print('app.config.root_path:', app.config.root_path) # debug #
# 根据传入的config决定使用哪个配置:来自instance目录(app.config.root_path)
# 在flask run时执行错误!此时config的类型为ScriptInfo,是不存在get()函数的
# 目前传入为字典类型,后面可以更改为ScriptInfo类型,官文说它未来有很大用处
# config还可以为None
if isinstance(config_fn, dict):
# 获取配置中的参数
instance_config = config_fn.get('config_fn') # 获取成功!
# 但不确定是否为文件名、文件是否有效,将使用app.config.from_pyfile(instance_config)配置应用
# 需要做异常判断
#
# 非None、str类型判断(是否可以为bytes?)
if instance_config and isinstance(instance_config, str):
try:
app.config.from_pyfile(instance_config)
except FileNotFoundError as e:
print('错误:参数config_fn:', config_fn)
print('错误:配置文件未找到!')
print(e)
except Exception as e:
# 输出错误信息
print('错误参数config_fn:', config_fn)
print('错误:其它错误!')
print(e)
else:
# 配置成功,输出提示消息
print('消息:使用%s配置app!' % instance_config)
else:
# flask run命令执行时config_fn一般为None,不做处理
# flask run命令执行时,怎么判断并使用项目的instance目录下的配置文件呢?
print('警告:参数config_fn的类型(%s)并非字典!' % type(config_fn))
print('警告:将使用默认配置文件dev.py进行配置!')
# 使用项目根目录下的配置文件dev.py进行配置
# 需要确保此文件存在,否则,数据库访问会不成功,
# 找到动态配置的方法再改进
# 程序中执行时也会传送空参数
try:
app.config.from_pyfile('dev.py')
except Exception as e:
print('错误:默认配置dev.py不存在,无法使用其完成配置!')
else:
print('警告:使用默认dev.py配置成功!') # 现在,亟需知道flask run传入参数的方式!
# 环境变量?WEBNEWS_CONF_FILE?
# app.config.from_envvar(‘APP_CONFIG_FILE’)将加载由环境变量APP_CONFIG_FILE指定的文件。
# 这个环境变量的值应该是一个配置文件的绝对路径。
# TBD # 应用扩展、蓝图配置等
from . import mdb, news mdb.init_app(app) app.register_blueprint(news.bpnews) 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。显然,这样的默认值是不试用的。
再次更新后的代码:
from flask import Flask def create_app(config_fn = None):
'''
创建Flask应用,并使用用户提供的instance目录下的配置文件配置应用
参数config_fn格式:{'config_fn':'filename.py'}
注意:请确定配置文件存在,否则,将不会发生配置
注意:请确定运行时的instance目录是否存在
'''
# 注意:instance_relative_config这个参数很重要!关系到是否使用instance目录!
app = Flask(__name__, instance_relative_config=True) print('app.config.root_path = ', app.config.root_path) # debug # 从config包下的default.py文件获取配置信息
app.config.from_object('config.default') # 根据传入的config决定使用哪个配置:来自instance目录(app.config.root_path)
# 目前使用字典方式传入配置文件名:
# {'config_fn':'dev.py'}
if isinstance(config_fn, dict):
# 获取配置中的参数
instance_config = config_fn.get('config_fn') # 获取成功!
# 但不确定是否为文件名、文件是否有效,将使用app.config.from_pyfile(instance_config)配置应用
# 需要做异常判断
#
# 非None、str类型判断(是否可以为bytes?)
if instance_config and isinstance(instance_config, str):
try:
# 使用配置文件配置应用
app.config.from_pyfile(instance_config)
except FileNotFoundError as e:
print('错误:参数config_fn:', config_fn)
print('错误:配置文件未找到!')
print(e)
except Exception as e:
# 输出错误信息
print('错误参数config_fn:', config_fn)
print('错误:其它错误!')
print(e)
else:
# 配置成功,输出提示消息
print('消息:使用%s配置app!' % instance_config)
else:
# 参数不符合本函数定义的规则
print('警告:参数config_fn的类型(%s)并非字典!' % type(config_fn)) # 应用扩展、蓝图配置等
from . import mdb, news mdb.init_app(app) app.register_blueprint(news.bpnews) 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官文配置参考链接: