Flaskにおける設定ファイルのベストプラクティス


## 概要

Flaskアプリケーションで設定ファイルを作成する際に
環境ごとの分割や内容の隠蔽をどうすればうまくいくかの検討をしていました。

[公式ドキュメント](http://flask.pocoo.org/docs/0.11/config/)と[海外の方の記事](http://damyanon.net/flask-series-configuration/)を読んで思ったことをまとめます。
このページの大部分は以上の2ページの和訳から作られています。

## Flaskにおけるconfig

Flaskの設定ファイルで押さえておいたほうが良さそうな部分をまとめます。

### 基本的な設定

`Flask`インスタンスの`config`変数に辞書形式で書き込むことができます。

```python:基本的な設定
app.config['DEBUG'] = True
```

### ファイルからの設定

指定したファイルから設定の一覧を読み込むことができます。

```python:ファイルからの設定
# ファイルパスを直接指定するパターン
app.config.from_pyfile('config_file.cfg')

# ファイルの位置を示す環境変数（絶対パス）を指定するパターン
app.config.from_envvar('FLASK_CONFIG_FILE')
```

ファイル内では以下のようなINIファイルの形式で設定を記載できます。

```config_file.cfg
TESTING=False
DEBUG=True
```

### オブジェクトからの設定

Pythonのオブジェクトとして設定を読み込むことができます。

```python:オブジェクトからの設定
app.config.from_object('config.BaseConfig')
```

ソースコード中では`from_json`というのも見えた気がしましたが、無視します。
ファイル内では以下のように、Pythonスクリプトとして設定を構造化できます。

```python:config.py
class BaseConfig(object):
    DEBUG = False
    TESTING = False


class DevelopmentConfig(BaseConfig):
    DEBUG = True
    TESTING = True


class TestingConfig(BaseConfig):
    DEBUG = False
    TESTING = True
```

### インスタンスフォルダ

アプリケーション開発の際にはソースのバージョン管理が不可欠ですが
設定ファイルにはパスワードやAPIのアクセスキーなどの
バージョン管理に含めたくない情報が存在します。

Flaskにおいて「インスタンスフォルダ」は
特定の設定ファイルをバージョン管理から除外するための機能です。

`Flask`の初期化の際に、絶対パスでインスタンスフォルダを指定できます。

```python:インスタンスフォルダのパス指定
app = Flask(__name__, instance_path='/path/to/instance/folder')
```

この指定がない場合は、デフォルトの`instance`ディレクトリが
インスタンスフォルダとして認識されます。

以下のように記載した場合はインスタンスフォルダから相対パスで
設定ファイルを読み込むことができます。

```python:相対的なファイルの呼び出し
app = Flask(__name__, instance_relative_config=True)
app.config.from_pyfile('application.cfg', silent=True)
```

ここでは`instance/application.cfg`を参照しています。
`silent=True`により、ファイルが見つからなかった場合のエラーを抑制しています。

とはいえ、これだけでバージョン管理から除外できるわけではないので
**インスタンスフォルダのパスを**`.gitignore`**に記載するのを忘れてはいけません。**

## ベストプラクティス

公式ページに[ベストプラクティス](http://flask.pocoo.org/docs/0.11/config/#configuration-best-practices)の項がありますが
内容は短く、あまり具体的な記載はされていません。
（英語力の問題により、「テストしやすい方が良い」くらいしか読み取れず…）

一方で、[こちら](http://damyanon.net/flask-series-configuration/)の記事では以下のように記載されています。

> Good practice is to have a default configuration, which is under source control and to override it with sensitive and specific information kept in instance folders. For the default configuration you could use object-based configuration hierarchy(described in Object-based configuration section) and to manage which configuration object to load via environment variables:

* デフォルトの設定ファイルはバージョン管理下に置く
* デフォルトの設定はオブジェクトとして書き、環境変数を利用して読み込む
* センシティブな設定はインスタンスフォルダ内の設定で上書きする

最後の項目は`development`や`production`などを切り替えるためですね。
オブジェクトとして記載することにより、テストもしやすくなるはずです。

具体例としては以下のように記載されています。

```python:ベストプラクティス（非公式）
# 基本的な設定はバージョン管理下のconfig.pyにオブジェクトとして記載
config = {
    "development": "bookshelf.config.DevelopmentConfig",
    "testing": "bookshelf.config.TestingConfig",
    "default": "bookshelf.config.DevelopmentConfig"
}

def configure_app(app):
    # 環境変数を利用して読み込む設定ファイルを決定
    config_name = os.getenv('FLASK_CONFIGURATION', 'default')
    
    # 設定はオブジェクトとして読み込む
    app.config.from_object(config[config_name])
    
    # センシティブな設定はインスタンスフォルダ内の設定で上書きする
    app.config.from_pyfile('config.cfg', silent=True)
```

## まとめ

上記の設定をすべてに適用してると大変だと思うので

* Hello World!をやる程度なら`app.config`にベタ書きで指定
* 個人で作るような小さめのアプリケーションなら`config`ファイルで設定
* 複数の環境を用意するレベルならベストプラクティスに従う

というくらいの認識を持ちました。

長々書いてしまいましたが、結局は自分自身が納得できるかどうかですよね。
構成が汚くても、秘密のファイルが流出しなければとりあえずは問題なし。

アプリケーションの拡張に合わせて、より良いかたちを見つけられればと思います。