刚接触Flask,它的一大特色简直不要太吸引人——5行代码写一个服务

from flask import Flask

app = Flask(__name__)


@app.route("/")
def index():
    return 'hello world!'


if __name__ == '__main__':
    app.run()

然而这么简单的启动方法居然被弃用了,还有王法吗?还有法律吗?

flask 开启redis flask 启动_flask 开启redis

正儿八经的启动

下面是一个简陋的项目文件结构

flask 开启redis flask 启动_应用实例_02

test_flask:项目根目录

TestFlask:主应用包

.flaskenv:存储用于Flask的环境变量

Pipfile、Pipfile.lock、.venv:当前项目虚拟环境

TestFlask.__init__.py文件内容

from flask import Flask


def create_app(config_name=None):
    app = Flask('TestFlask')
    return app

.flaskenv文件内容

FLASK_APP=TestFlask
FLASK_ENV=development

然后在pipenv虚拟环境中安装 python-dotenv(不知道怎么用pipenv的花5分钟看下)

pipenv install python-dotenv

最后配置一个启动配置

flask 开启redis flask 启动_flask_03

然后运行刚才配置的启动项就可以跑起来了

Flask自动发现应用实例机制

Flask是怎么发现项目那个地方有app(应用实例)的呢?

flask run命令执行时,会根据自动发现机制寻找应用实例:

  1. 从当前目录寻找app.py模块,并在该模块中寻找创建的应用实例(变量名不限,通常为app),或者是名为create_app或者make_app的工厂函数,自动调用后获得应用实例。
  2. 从环境变量FLASK_APP指向的位置,寻找创建的应用实例(变量名不限,通常为app),或者是名为create_app或者make_app的工厂函数,自动调用后获得应用实例。
  3. 当安装了python-dotenv时,会在当前目录寻找.flaskenv或者.env文件中寻找FLASK_APP环境变量,并通过其值指向的位置寻找名为create_app或者make_app的工厂函数,自动调用后获得应用实例。

正儿八经的优势

如果废了这么大劲只是为了换个姿势启动,那是和脱裤子放P没啥区别

所以换个姿势,是为了找个更好的角度发力

方便测试和部署

不用写死配置,而是通过工厂函数,可以在不同的环境(开发、测试、生产),传入对应的参数,从而根据不同的配置创建应用实例,实现配置隔离的效果

工厂函数

def create_app(config_name=None):
    if config_name is None:
        config_name = os.getenv('FLASK_CONFIG', 'development')

    app = Flask('bluelog')
    app.config.from_object(config[config_name])

配置模块

class BaseConfig(object):
    ...


class DevelopmentConfig(BaseConfig):
    ...


class TestingConfig(BaseConfig):
    ...


class ProductionConfig(BaseConfig):
    ...

config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig
}

管理注册的扩展

当程序注册的扩展比较多时,通过在工厂函数中实现扩展的初始化,和在extensions.py模块中实现扩展的实例化,可以将扩展的引入动作模块化,结构更清晰,方便管理

在工厂函数中

def create_app(config_name=None):
    app = Flask('bluelog')
    ...
    register_extensions(app)
    ...
    return app


def register_extensions(app):
    bootstrap.init_app(app)
    db.init_app(app)
    login_manager.init_app(app)
    csrf.init_app(app)
    ckeditor.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    toolbar.init_app(app)
    migrate.init_app(app, db)

在extensions.py模块中

bootstrap = Bootstrap()
db = SQLAlchemy()
login_manager = LoginManager()
csrf = CSRFProtect()
ckeditor = CKEditor()
mail = Mail()
moment = Moment()
toolbar = DebugToolbarExtension()
migrate = Migrate()

项目其他地方需要使用扩展从extensions.py模块中引入,但是由于扩展的初始化是在工厂函数中完成的,所以扩展的后续使用也需要在程序上下文激活的环境使用

命令行参数

flask run命令和app.run()方法底层都是调用run_simple方法,所以原先通过run()方法传参实现的功能也可以通过命令行传参实现

-h, --host TEXT                 The interface to bind to.
   -p, --port INTEGER              The port to bind to.
   --cert PATH                     Specify a certificate file to use HTTPS.
   --key FILE                      The key file to use when specifying a
                                   certificate.  --reload / --no-reload          Enable or disable the reloader. By default
                                   the reloader is active if debug is enabled.  --debugger / --no-debugger      Enable or disable the debugger. By default
                                   the debugger is active if debug is enabled.  --eager-loading / --lazy-loader
                                   Enable or disable eager loading. By default
                                   eager loading is enabled if the reloader is
                                   disabled.  --with-threads / --without-threads
                                   Enable or disable multithreading.
   --extra-files PATH              Extra files that trigger a reload on change.
                                   Multiple paths are separated by ';'.  --help                          Show this message and exit.