刚接触Flask,它的一大特色简直不要太吸引人——5行代码写一个服务
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return 'hello world!'
if __name__ == '__main__':
app.run()
然而这么简单的启动方法居然被弃用了,还有王法吗?还有法律吗?
正儿八经的启动
下面是一个简陋的项目文件结构
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自动发现应用实例机制
Flask是怎么发现项目那个地方有app(应用实例)的呢?
flask run命令执行时,会根据自动发现机制寻找应用实例:
- 从当前目录寻找app.py模块,并在该模块中寻找创建的应用实例(变量名不限,通常为app),或者是名为create_app或者make_app的工厂函数,自动调用后获得应用实例。
- 从环境变量FLASK_APP指向的位置,寻找创建的应用实例(变量名不限,通常为app),或者是名为create_app或者make_app的工厂函数,自动调用后获得应用实例。
- 当安装了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.