# Flask實作_ext_05_Flask-Script ###### tags: `flask` `flask_ext` `python` :::danger 官方文件: * [flask-script](https://flask-script.readthedocs.io/en/latest/) ::: 在編寫文件的同時,Flask已發佈正式版,但Flask-Script並未積極更新,這部份官方文件上已有警訊,建議作法是轉使用CLI。 ![](https://i.imgur.com/0RgrFCT.png) ## 說明 在開發Flask的時候時常需要一直測試、資料庫重建、或是測試過程中的資訊流是否正確,`db.create_all()`應該就是不少人執行過最多的程序,標準來說該怎麼處理? ```python= # 需要在執行cmd之後先執行python進入Python Shell from xxx import db db.create_all() ``` 但是在`flask-script`中可以先設置好相關指令,再透過簡單的命令列來做操作。 ```shell python manager.py your_command ``` `flask-script`允許我們定義好相關命令之後利用command來執行,這樣的說明似乎很抽象,利用下面的簡單範例可以快速瞭解。 ## 安裝 ```shell= pip install flask-script ``` ## 範例 ### 簡單範例 新增一個Python文件,命名為`test-script.py`,如下: ```python= from flask import Flask from flask_script import Manager app = Flask(__name__) DBScritp = Manager(app) @DBScritp.command def init_db(): print('init database success') @app.route('/') def hello_world(): return 'Hello World!' if __name__ == '__main__': DBScritp.run() ``` 第6行:實作上習慣會以小寫`manager`,為了避免混肴以`DBScript`來實作 第10行:利用裝飾器定義一個function `init_db` 第20行:啟動程序調整以`DBScript`來啟動 來測試實際執行的狀況,單純的利用command即可,如下: ```shell= >>> (venv) D:\proPycharm\test-script>python test-script.py init_db init database success ``` 第1行:執行命令 第2行:確實的回傳定義的資訊 使用`flask-script`之後,啟動主程式必需執行由`flask-script`代理所在的Python,後面再加上稍早所定義的命令`init_db`,我們發現到這樣子的方式執行命令確實的回傳了定義的資訊。 這代表可以在裡面定義任何我們想要的東西,資料庫的建置,或某一個變數在啟動之後的狀態...ext ### 簡單範例_繼承類別 相同的工作我們還可以透過繼承類別`Command`來達成,如下: ```python= # 追加載入Command from flask_script import Command class MyCommand(Command): print('MyCommand Class') def run(self): print('MyCommand Method') # 註冊自定義的類別 DBScritp.add_command('mycommand', MyCommand()) ``` 第2行:import Command 第4行:自定義指令,繼承類別`Command` 第7行:一定要定義一個命名為`run`的function 第11行:註冊自定義的指令類別,第一個參數為指令命名,第二個參數為自定義類別 設置完畢之後,利用Command來測試,如下: ```shell= >>> (venv) D:\proPycharm\test-script>python test-script.py mycommand MyCommand Class MyCommand Method ``` ### 指令加入參數 自定義指令中如果需求傳遞參數,這部份可以透過`option`來完成,如下: ```python= @DBScritp.option('-n', '--para', help='input parameter') def param(para): print("It's Your parameter %s" % para) ``` 第1行:利用`DBScritp.option`來設罝參數跟help,-n是後面--para的簡碼 實作測試如下: ```shell= >>> (venv) D:\proPycharm\test-script>python test-script.py param --para 100 MyCommand Class It's Your parameter 100 ``` ```shell= >>> (venv) D:\proPycharm\test-script>python test-script.py param -n 100 MyCommand Class It's Your parameter 100 ``` ```shell= >>> (venv) D:\proPycharm\test-script>python test-script.py param --help MyCommand Class usage: test-script.py param [-?] [-n PARA] optional arguments: -?, --help show this help message and exit -n PARA, --para PARA input parameter ``` 相同模式也可以利用`@DBScript.command`來實作,如下: ```python= @DBScritp.command def testcommand(servername='HP'): print('Server %s on' % servername) ``` 第2行:加入參數,預設為HP 測試如下: ```shell >>> (venv) D:\proPycharm\test-script>python test-script.py testcommand MyCommand Class Server HP on >>> (venv) D:\proPycharm\test-script>python test-script.py testcommand --servername IBM MyCommand Class Server IBM on ``` ### 互動指令 很多時候不小心手滑了一下結果下錯指令是很正常的事,即使強如葉小釵還是會因為手滑不小心殺掉萬古長空。因此在設置重要指令的同時如果可以多一個互動YES/NO的話,那就可以避免這類問題的發生。 ```python= from flask_script import prompt_bool @DBScritp.command def do_something(): if prompt_bool("Are you sure create new database"): print('You say yes') ``` 第1行:`prompt_bool`為用來產生對話的function 第5行:對話內容 測試: ```shell= >>> (venv) D:\proPycharm\test-script>python test-script.py do_something MyCommand Class Are you sure create new database [n]: y You say yes ``` 第3行:出現對話框,輸入`y` 第4行:選擇`y`之後執行命令,如果選擇`n`會直接中斷 ### Shell `Shell`用來命令模型debug可以省掉不下import的時間,也是很抽象的一句話,看下面範例: ```python= from flask_script import Manager, Command, prompt_bool, Shell from app_blog import db def _make_context(): return dict(app=app, db=db) DBScritp.add_command("shell", Shell(make_context=_make_context)) ``` 第1行:追加import `Shell` 第2行:追加import `db`或任何需求預先載入的物件 第4行:設置一個function return一個`dict`,裡面有兩個物件或更多 第7行:加入`command` ```shell= (venv) D:\proPycharm\test-script>python test-script.py shell >>> db <SQLAlchemy engine=sqlite:///d:\data_register.sqlite> >>> app <Flask 'test-script'> ``` 第1行:執行`shell` 上面案例可以發現到,執行`shell`之後會預先幫我們設置好的物件,非常方便。 另一種方式是直接透過裝飾器設置,如下: ```python= @DBScritp.shell def _make_context(): return dict(app=app, db=db) ``` ## 總結 一般來說`@DBScritp.command`已經滿足大部份應用場景,如果真的有參數需求,再透過`@DBScritp.option`來實作即可。 另外,如果在專案啟動的時候希望可以指定環境變數的話,也可以透過`add_option`來實作 ```python= manager.add_option('-c', '--config', dest='config', required=False) ``` 雖然該專案目前已經停止維護了,但在尚能應用的情況下還是非常的方便的,但是建議還是要試著將專案往CLI去維護,避免Flask後續更新的相容問題。 ## 其它功能 ### 查詢目前專案所有的url ```python= from flask_script.commands import ShowUrls DBScritp.add_command('show', ShowUrls()) ``` 第1行:import `ShowUrls` 第2行:註冊命令,並且命名為`show` ```shell= >>> (venv) D:\proPycharm\test-script>python test-script.py show Rule Endpoint ------------------------------------ / hello_world /static/<path:filename> static ``` 執行之後就可以很清楚的看的到整個專案的路由,這對後續導入`flask-blueprint`很有幫助。