Abstrct
flask從0.11版本開始引入了click提供命令行支持,在此之前我們通常會引入Flask-Script來提供。
在《Flask web開發》這本書編寫時flask0.11還沒有發布,因此書中仍然以flask-script提供命令行支持。因此在flask0.11發布一年后,作者寫了這篇文章來幫助大家從flask-script遷移到Flask-Cli,該博文便是作者這篇文章的翻譯。
作者對於Click引入flask的意見
回到2014年,當Armin Ronacher向我介紹將Click整合進flask,我是(現在也是)拒絕的,這給Flask內核項目添加了別的依賴,我建議基於click實現命令行支持作為flask的拓展,與flask-script進行公平的競爭。你可以查看該討論在GitHub的issue中 https://github.com/smurfix/flask-script/issues/97。
因此我確信,強制命令行工具基於Click是個錯誤,並且違反了flask的基本准則‘將最優秀的工具用於每一項任務’ 。而且不幸的是,click成為flask核心的一部分使flask-script的維護者離開了,而且從github的repository來看,這個項目似乎正在緩慢死亡
那么,flask-script有什么問題呢?
flask確實有着一個重要但是同時卻足夠有趣的缺陷,由於早期設計的flask reloader的問題,你將直接使用app.run()啟動你的應用而不是Flask-Script
如果你以debug模式啟動flask,那么將有兩個flask進程被運行。第一個進程(我們可以稱他為watcher)將會觀察源代碼的運行,第二個進程才是實際的flask服務器。當任何文件被修改時,這個watcher一旦觀察到文件改變將會kill服務進程,然后啟動一個使用已經更新的源代碼的服務器進程。當你更新的某個源文件中存在語法錯誤時,會出現一個問題。Watcher進程並不知道源碼是否更好,它會kill舊的服務器進程,運行一個新的服務器進程。但是新的服務器進程卻不會運行成功,因為python解析修改后的文件時會拋出錯誤。當服務進程存在在錯誤時,這個watcher進程也會退出,直到你修復了bug並重啟。
在你使用新的命令flask run命令行時load將會很完美,直到你第一個request發來之前,應用都不會被加載,當引入源碼文件有錯誤時,他將會被在運行時處理,這意味着你可以使用基於web的debug工具。新的watcher進程也更加智能,在服務端被kill后仍然會堅持監控源碼的變化,並在源碼發生改變之后重啟服務。
新的Flask-CLi
Flask-Cli比Flask-Script更加優秀嗎?讓我們回顧下新的Flask應用提供的默認命令吧
當你安裝Flask0.11或者更新的版本后,你的環境變量將會存在flask命令
(venv) $ flask
Usage: flask [OPTIONS] COMMAND [ARGS]...
This shell command acts as general utility script for Flask applications.
It loads the application configured (through the FLASK_APP environment
variable) and then provides commands either provided by the application or
Flask itself.
The most useful commands are the "run" and "shell" command.
Example usage:
$ export FLASK_APP=hello.py
$ export FLASK_DEBUG=1
$ flask run
Options:
--version Show the flask version
--help Show this message and exit.
Commands:
run Runs a development server.
shell Runs a shell in the app context.
當我們使用Flask-Script時,你將創建名為manage.py的驅動腳本。我們來看下flask-script與flask-cli之間的對比
#flask-script -> flask-Cli
./manage.py runserver -> flask run
./manage.py shell ->flask shell
看上去似乎沒什么區別。
然和事實上,manage.py和flask發現我們Flask應用實例的方式是完全不一樣的。對於Flask-Script提供了Manager類直接在我們的應用工廠中使用。然和新的Flask-Cli卻沒用提供應用實例而是設定FLASK_APP的環境變量,一般用來設定文件名或者模塊名。FLask將會尋找模塊中的app或者application的實例來使用。
可惜,Flask-Cli沒有直接為工廠函數提供支持,這個方法意味着你如果需要使用工廠函數你必須定義一個工廠函數模塊來創建你的app對象,然后再FLASK_APP中引用。這與wsgi.py模塊在Django中的概念是一致的
如果你想為Flasky應用提供FLask Cli的支持,你可以在flasky.py中這樣編寫
import os
from app import create_app
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
我們在將會會完全遷移至新的Cli,不在使用manage.py來。
Windows設置FLASK_APP
set FLASK_APP = flasky.py
flask run
flask run命令提供了配置來啟用、禁用reloader和debugger,當然你也可以設置IP 的address和port來控制服務器的監聽。這些配置雖然與Flask-Script相近但並不完全相同,可以使用flask run -help來查看提供的配置。
大部分應用可以使用下面的方式啟動
if __name__ == '__main__':
app.run()
當你不需要使用CLI支持時你確實可以以這種方式啟動。但是當你確實需要新的cli時,你盡可以移除它。
使用Flask Shell 命令
shell命令基本上是相同的,但是在如何定義要自動導入shell上下文中的其他symbols有輕微的差別。這個功能可以節省你在應用程序工作時的大量時間。通常,你在shell中的測試或調試shell中添加model類、數據庫實例和其他可能與之交互的對象。
對於FLask-Script,Flasky應用有如下shell上下文定義
def make_shell_context():
return dict(app=app, db=db, User=User, Follow=Follow, Role=Role,
Permission=Permission, Post=Post, Comment=Comment)
manager.add_command("shell", Shell(make_context=make_shell_context))
正如你在上文看到的,make_shell_context()函數被add_command引用來定義shell參數。Flask-Script在啟動shell前直接調用這個函數,然后返回一個包含這些symbol的字典。
Flask CLI提供了相同功能,但是確實以decorator(裝飾器)來判斷是不是shell上下文對象。為了和FLask-Script有相同的功能,我們擴展了flasky.py模塊。
import os
from app import create_app, db
from app.models import User, Follow, Role, Permission, Post, Comment
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
@app.shell_context_processor
def make_shell_context():
return dict(app=app, db=db, User=User, Follow=Follow, Role=Role,
Permission=Permission, Post=Post, Comment=Comment)
正如你看見的,作用時相同的,但是你需要@app.shell_context_processor裝飾器使Flask知道這些shell上下文對象
增加Flask拓展的命令
Flask-Script取得成功的一個很大關鍵在於允許Flask拓展添加他們自己的命令。Flask-Migrate庫就最大限度利用了這一個優點。
所以如何遷移這些命令到flask-cli呢?這取決於拓展的作者是如何做遷移的,如果你使用的擴展正未曾為FLask Cli做出適配,那么你得繼續使用FLask-Script,至少當你與這些拓展直接進行交互時得這樣。Flask migrate提供了對於FLask Cli的更新,直接使用它最新的版本即可。
對於Flask Migrate, ./manage.py db 直接變成了 flask db。在Flask-Script中我們Flask-Migrate在manage.py中被初始化,在FLaskCli中我們需要將Flask-Script版本中manage.py中的內容移入flasky.py中,只有一些微小的不同。
import os
from app import create_app, db
from app.models import User, Follow, Role, Permission, Post, Comment
from flask_migrate import Migrate
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
migrate = Migrate(app, db)
@app.shell_context_processor
def make_shell_context():
return dict(app=app, db=db, User=User, Follow=Follow, Role=Role,
Permission=Permission, Post=Post, Comment=Comment)
在示例中,我們使用接下來的命令更新flasky的數據庫到最新的版本。(不要忘記設置FLASK_APP)
flask db upgrade
應用命令
Flask Script另一個優秀的特點是允許你把自己的常用任務(custom work)封裝函數在命令行中執行。在CLI中你也只需要添加一個裝飾器就能夠完成遷移
原先的./manage.py test命令
@manager.command
def test(coverage=False):
"""Run the unit tests."""
# ...
在Flask-Cli中
import click
# ...
@app.cli.command()
@click.option('--coverage/--no-coverage', default=False, help='Enable code coverage')
def test(coverage):
"""Run the unit tests."""
# ...
@app.cli.command()裝飾器提供了接口給Click。因為在Flask-Script中命令函執行是在應用上下文中的,而Flask-Cli中應用上下文如果你不需要是可以禁用的。在此處,函數的實際代碼不需要改變,但注意coverage選項需要顯式使用@click.option裝飾器,而在flask-script中,該選項是自動導出的函數的參數列表。
Flasky其他兩個函數也可以使用同樣的方法,flasky模塊的完全實現如下
import os
from app import create_app, db
from app.models import User, Follow, Role, Permission, Post, Comment
from flask_migrate import Migrate
import click
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
migrate = Migrate(app, db)
@app.shell_context_processor
def make_shell_context():
return dict(app=app, db=db, User=User, Follow=Follow, Role=Role,
Permission=Permission, Post=Post, Comment=Comment)
@app.cli.command()
@click.option('--coverage/--no-coverage', default=False, help='aaa')
def test(coverage=False):
"Test coverage"
# ...
@app.cli.command()
@click.option('--length', default=25, help='Profile stack length')
@click.option('--profile-dir', default=None, help='Profile directory')
def profile(length, profile_dir):
"""Start the application under the code profiler."""
# ...
@app.cli.command()
def deploy():
"""Run deployment tasks."""
# ...
總結
在本文中,我向您展示了新的CLI命令行所提供的功能,這有助於你從Flask-Script中遷移。還有兩件事本文並沒有提及,
如果你不想使用flask命令,如何創建類似於flask-script的manage.py的你自己的驅動腳本?
如何通過工程中”setup.py"中的’Entry points“注冊命令?
你可以查閱[CLI Documention](CLI documenation )來了解他們
