---
# System prepended metadata

title: Flask實作_ext_05_Flask-Script
tags: [flask, flask_ext, python]

---

# 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`很有幫助。