# Flask實作_ext_05_Flask-Script
###### tags: `flask` `flask_ext` `python`
:::danger
官方文件:
* [flask-script](https://flask-script.readthedocs.io/en/latest/)
:::
在編寫文件的同時,Flask已發佈正式版,但Flask-Script並未積極更新,這部份官方文件上已有警訊,建議作法是轉使用CLI。

## 說明
在開發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`很有幫助。