zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    布局
    面向对象....(概况)
    下拉城市列表
    下拉列表
    内容窗体
    《面向对象》-继承
    《面向对象》--类
    《面向对象》第一讲
    常用的正则表达式
    正则表达式
  • 原文地址:https://www.cnblogs.com/luo630/p/9321847.html
Copyright © 2011-2022 走看看