
# 配置
當你開始學習Flask時,配置看上去是小菜一碟。你僅僅需要在*config.py*定義幾個變量,然后萬事大吉。
然而當你不得不管理一個生產上的應用的配置時,這一切將變得棘手萬分。
你不得不設法保護API密鑰,或者糾結于為了不同的環境(比如開發環境和生產環境)使用不同的配置。
在本章我們將探討Flask的一些高級特性,它們能讓配置管理更為輕松。
## 從小處起步
一個簡單的應用不需要任何復雜的配置。你僅僅需要在你的根目錄下放置一個*config.py*文件,并在*app.py*或*yourapp/\_\_init\_\_.py*中加載它。
*config.py*的每一行中應該是某一個變量的賦值語句。一旦*config.py*在稍后被加載,這個配置變量可以通過`app.config`字典來獲取,比如`app.config["DEBUG"]`。
以下是一個小項目的*config.py*文件的范例:
```python
DEBUG = True # 啟動Flask的Debug模式
BCRYPT_LEVEL = 13 # 配置Flask-Bcrypt拓展
MAIL_FROM_EMAIL = "robert@example.com" # 設置郵件來源
```
有一些配置變量是內建的,比如`DEBUG`。還有些配置變量是關于Flask拓展的,比如`BCPYRT_LEVEL`就是用于Flask-Bcrypt拓展(一個用于hash映射密碼的拓展)。
你甚至可以定義在這個應用中用到的自己的配置變量。
在這個例子中,我使用`app.config["MAIL_FROM_EMAIL"]`來表示郵件往來時(比如重置密碼)默認的發送方。
這使得在將來要修改的時候不會帶來太多麻煩。
為了加載這些配置變量,我通常使用`app.config.from_object()`。如果是單一模塊應用中,是在*app.py*;或者在*yourapp/\_\_init\_\_.py*,如果是基于包的應用。
無論在哪種情況下,代碼看上去像這樣:
```
from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
# 現在通過app.config["VAR_NAME"],我們可以訪問到對應的變量
```
### 一些重要的配置變量
| 變量 | 描述 | 默認值|
| -------------|:-------------:|------- |
| DEBUG | 在調試錯誤的時候給你一些有用的工具。比如當一個請求導致異常的發生時,會出現的一個web界面的調用堆棧和Python命令行。| 在開發環境下應該設置成True,在生產環境下應設置為False。|
| SECRET_KEY | Flask使用這個密鑰來對cookies和別的東西進行簽名。你應該在*instance*文件夾中設定這個值,并不要把它放入版本控制中。你可以在下一節讀到關于*instance*文件夾的更多信息。| 這應該是一個復雜的任意值。|
| BCRYPT_LEVEL | 如果使用Flask-Bcrypt來hash映射用戶密碼(如果沒有,現在就用它),你需要為hash密碼的算法指定“rounds”的值。設置的rounds值越高,計算一次hash花費的時間就越長(同樣的效果作用于破解方,這個才是重要的)。rounds的值應該隨著你的設備的計算能力的提升而增加| 如果使用Flask-Bcrypt來hash映射用戶密碼(如果沒有,現在就用它),你需要為hash密碼的算法指定“rounds”的值。設置的rounds值越高,計算一次hash花費的時間就越長(同樣的效果作用于破解方,這個才是重要的)。rounds的值應該隨著你的設備的計算能力的提升而增加|
** 確保生產環境下已經設置了 `DEBUG = False`。如果忘記關掉,用戶會很樂意對你的服務器執行任意的Python代碼。**
## instance文件夾
有時你需要定義一些不能為人所知的配置變量。為此,你會想要把它們從*config.py*中的其他變量分離出來,并保持在版本控制之外。
你可能要隱藏類似數據庫密碼和API密鑰的秘密,或定義特定于當前機器的參數。
為了讓這更加輕松,Flask提供了一個叫*instance文件夾*的特性。
instance文件夾是根目錄的一個子文件夾,包括了一個特定于當前應用實例的配置文件。我們不要把它提交到版本控制中。
這是一個使用了instance文件夾的簡單Flask應用的結構:
```
config.py
requirements.txt
run.py
instance/
config.py
yourapp/
__init__.py
models.py
views.py
templates/
static/
```
### 使用instance文件夾
要想加載定義在instance文件夾中的配置變量,你可以使用`app.config.from_pyfile()`。
如果在調用`Flask()`創建應用時設置了`instance_relative_config=True`,`app.config.from_pyfile()`將查看在*instance*文件夾的特殊文件。
```python
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config')
app.config.from_pyfile('config.py')
```
現在,你可以在*instance/config.py*中定義變量,一如在*config.py*。
你也應該將instance文件夾加入到版本控制系統的忽略名單中。比如假設你用的是git,你需要在*gitignore*中新開一行,寫下`instance/`。
### 密鑰
instance文件夾的隱秘屬性使得它成為藏匿密鑰的好地方。
你可以在放入應用的密鑰或第三方的API密鑰。假如你的應用是開源的,或者將會是開源的,這會很重要。我們希望其他人去使用他們自己申請的密鑰。
```python
# instance/config.py
SECRET_KEY = 'Sm9obiBTY2hyb20ga2lja3MgYXNz'
STRIPE_API_KEY = 'SmFjb2IgS2FwbGFuLU1vc3MgaXMgYSBoZXJv'
SQLALCHEMY_DATABASE_URI= \
"postgresql://user:TWljaGHFgiBCYXJ0b3N6a2lld2ljeiEh@localhost/databasename"
```
### 最小化依賴于環境的配置
如果你的生產環境和開發環境之間的差別非常小,你可以使用你的instance文件夾抹平配置上的差別。
在*instance/config.py*中定義的變量可以覆蓋在*config.py*中設定的值。
你只需要在`app.config.from_object()`之后才調用`app.config.from_pyfile()`。
這樣做的其中一個優點是你可以在不同的機器中修改你的應用的配置。你的開發版本庫可能看上去像這樣:
config.py
```
DEBUG = False
SQLALCHEMY_ECHO = False
```
instance/config.py
```
DEBUG = True
SQLALCHEMY_ECHO = True
```
然后在生產環境中,你將這些代碼從*instance/config.py*中移除,它就會改用回*config.py*中設定的變量。
> **參見**
> * 在這里可以讀到關于Flask-SQLAlchemy的配置密鑰: http://pythonhosted.org/Flask-SQLAlchemy/config.html#configuration-keys
## 依照環境變量來配置
instance文件夾不應該在版本控制中。這意味這你將不能追蹤你的instance配置。
在只有一兩個變量的情況下這不是什么問題,但如果你有關于多個環境(生產,穩定,開發,等等)的一大堆配置,你不會愿意冒失去它們的風險。
Flask給我們提供了根據環境變量選擇一個配置文件的能力。
這意味著我們可以在我們的版本庫中有多個配置文件,并總是能根據具體環境,加載到對的那個。
當我們到了有多個配置文件共存的境況,是時候把文件都移動到`config`包之下。
下面是在這樣的一個版本庫中大致的樣子:
```
requirements.txt
run.py
config/
__init__.py # 空的,只是用來告訴Python它是一個包。
default.py
production.py
development.py
staging.py
instance/
config.py
yourapp/
__init__.py
models.py
views.py
static/
templates/
```
在我們有一些不同的配置文件的情況下,可以這樣設置:
| 文件名 | 內容 |
| ------------------ |------------- |
| config/default.py | 默認值,適用于所有的環境或交由具體環境進行覆蓋。舉個例子,在*config/default.py*中設置`DEBUG = False`,在*config/development.py*中設置`DEBUG = True`。|
| config/development.py| 在開發環境中用到的值。這里你可以設定在localhost中用到的數據庫URI鏈接。|
| config/production.py | 在生產環境中用到的值。這里你可以設定數據庫服務器的URI鏈接,而不是開發環境下的本地數據庫URI鏈接。|
| config/staging.py | 在你的開發過程中,你可能需要在一個模擬生產環境的服務器上測試你的應用。你也許會使用不一樣的數據庫,想要為穩定版本的應用替換掉一些配置。|
要在不同的環境中指定所需的變量,你可以調用`app.config.from_envvar()`:
```python
# yourapp/__init__.py
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('config.default')
app.config.from_pyfile('config.py') # 從instance文件夾中加載配置
app.config.from_envvar('APP_CONFIG_FILE')
```
`app.config.from_envvar(‘APP_CONFIG_FILE’)`將加載由環境變量`APP_CONFIG_FILE`指定的文件。這個環境變量的值應該是一個配置文件的絕對路徑。
這個環境變量的設定方式取決于你運行你的應用的平臺。如果你是在一臺標準的Linux服務器上運行,你可以使用一個shell腳本來設置環境變量并運行`run.py`。
start.sh
```
APP_CONFIG_FILE=/var/www/yourapp/config/production.py
python run.py
```
*start.sh*特定于某個環境,所以它也不能放入版本控制當中。如果你把應用托管到Heroku,你可以用Heroku提供的工具設置環境變量參數。對于其他PAAS平臺也是同樣的處理。
## 總結
* 一個簡單的應用也許僅需一個配置文件:*config.py*
* instance文件夾可以幫助我們隱藏不愿為人所知的配置變量。
* instance文件夾可以用來改變特定環境下的程序配置。
* 應對復雜的,基于環境的配置,我們可以結合環境變量和`app.config.from_envvar()`來使用。