Flaskのディレクトリ構成について考えてみたらBlueprintsを試していた話

## 概要

この記事は、Flaskアプリについて、どういうディレクトリ構成ならびにファイル構成にするのが良いのか、という点について世間一般はどうしてるのかと調べてみたメモ書きです。
まぁ、頑張ってつくりこめばつくりこむほど、DjangoとかRailsとかに寄っていくんだろうなぁ……とは思ってます。

**注) 有用なこと書いてなくても許してくださぃ**

## Flask解説サイトでお勧めされていたディレクトリ構成

[ExploreFlask][] というサイトを確認すると、多少複雑なプロジェクトを作るなら、モデルやフォーム用のクラスを作るべきで、
複数のモジュールを繋ぎ合わせたPythonパッケージとして作るのが良いから、以下のような構成にすると良い的な事が書いてありました。

```
config.py
requirements.txt
run.py
instance/
    config.py
yourapp/
    __init__.py
    views.py
    models.py
    forms.py
    static/
    templates/
```

各ファイル/ディレクトリについては、以下の表のような感じだそうです。

| ファイル/ディレクトリ名 | 使い方 |
| :--- | :-- |
| run.py | 開発用サーバの実行用スクリプトでプロダクション用としては使わない |
| requirements.txt | 依存するパッケージを書く |
| config.py | 設定変数を格納したファイル |
| /instance/config.py | APIキーやデータベースのURIなど、バージョン管理に含めない設定変数を格納したファイル |
| /yourapp/ | アプリの格納用 |
| /yourapp/\_\_init\_\_.py | アプリの初期化用スクリプトで、様々なコンポーネントを読み込む |
| /yourapp/view.py or /yourapp/views/ | routeを定義するファイルもしくはディレクトリ |
| /yourapp/models.py or /yourapp/models/ | アプリのモデル(のクラス)を定義するファイルもしくはディレクトリ |
| /yourapp/forms.py (or /yourapp/forms/ ? ) | フォームの定義をするファイル(もしくはディレクトリ?) (なぜかこれだけ参考サイトの表からディレクトリが消えてました)| 
| /yourapp/static/ | 公開用のCSS, JavaScript, 画像等のアプリで公開すべきファイル置き場 |
| /yourapp/templates | Jinja2テンプレートを置くんだ (なぜJinja2決めうちなんざましょ……) |

に書くということのようです。
なお、さらに複雑なアプリの場合、さらに分割するのもアリなようです。
……`@app.cli.command` で定義できるようなcliなコマンドとかはどこに置くんですかね? また別ファイルでしょうか?
あと、どうでもいいですが、 `__init__.py` が表の中で強調表示になっていて(単なるエスケープ忘れ)イラっとしました。

## Blueprintsをつかってみた

とここまで書いたように、ある程度複雑なアプリを書く場合はファイル等を分割して書く方が良い(というのは、ある程度のプログラムを書いた人なら感覚的に当然と思えるかと思う)のですが、
Flaskアプリで色々と分割しだす場合、[Blueprints][Blueprints] というのを使うのが良いようです。

[こちらのサイト][ExploreFlaskBlueprints]の説明を拝借すると、「Flaskアプリに適用できるようなビュー、テンプレート、静的ページなどのコレクションを定義できる仕組み」のようです。
まぁ、実際に使ってみた方が理解がはやい感じ。
まずはblueprintのオブジェクトを作るところからのようです。

```hoge/views/general.py
from flask import Blueprint

general = Blueprint('general', __name__)


@general.route('/')
def hello():
    return 'hello'


@general.route('/about')
def about():
    return 'about'
```

`Blueprint` モジュールの `Blueprint()` を使って作成できました。
`Blueprint()` の引数ですが、オブジェクトにつけたい名前と `__name__` を引数にするのが良いようです。
普通のFlaskのアプリと同じように、デコレータを使ってroute定義していくようです。

さて、ではこの作成したblueprintオブジェクトをどのように使うかという点なんですが、以下のようにFlaskオブジェクトの `regiseter_blueprint()` メソッドという、そのまんまなメソッドを使うようです。

```hoge/__init__.py
from flask import Flask
from .views.general import general

app = Flask(__name__)
app.register_blueprint(general)
```

これで、いつものようにサーバーを動かすと、いつものように http://localhost:5000/ や http://localhost:5000/about/ が表示されるようになりました。

```
% export FLASK_APP=hoge
% export FLASK_DEBUG=true
% python3 -m flask run
```

これだけだと、あんまり嬉しさがわからないんですが、blueprintを使うメリットが以下のようなコードを書くと「ふーむ」という感じになりました。

```hoge/__init__.py
from flask import Flask
from .views.general import general

app = Flask(__name__)
app.register_blueprint(general, url_prefix='/hoge')
```

ミソは、 `register_blueprint()` に渡している `url_prefix` という引数です。
これで先程 http://localhost:5000/ と http://localhost:5000/about/ で見えていたページが、それぞれ http://localhost:5000/hoge/ と http://localhost:5000/hoge/about/ で見えるようになりました。
なお、 `url_prefix` は Blueprint オブジェクトの作成時に `Blueprint()` に同名の引数としても渡してもいけるようですが、サイトでもお勧めされているように、registerの時につけてあげる方がわかりやすそうですかねぇ。

なお、 `url_prefix` には変数を書いても良いそうです。
具体例は以下のような感じ。

```hoge/views/general.py
from flask import Blueprint

general = Blueprint('general', __name__)


@general.route('/')
def hello(user):
    return 'hello %s' % user


@general.route('/reverse')
def about(user):
    return user[::-1]
```

```hoge/__init__.py
from flask import Flask
from .views.general import general

app = Flask(__name__)
app.register_blueprint(general, url_prefix='/<user>')
```
他にも、テンプレートや静的ファイルの場所も `Blueprint()` への引数で(登録時はどうなんでしょう?)設定できたりと、まだ色々と機能がありそうです。

## 所管

やっぱりRailsとかはディレクトリ構成が良く考えられてるんだなぁ、と思いました。
が、こういうのを自分で触ってからの方が理解しやすそうな気がします。
以前Rebuild.fmというPodcastで「Railsをいきなり触ると大変だからSinatraを勧めてみた」と伊藤直也さんがおっしゃっていたのは、的確なんだなぁ、とも。

とにもかくにも、まずは自分が作りかけているFlaskのアプリについて、ちょっと書き直してみようと思います。

## 参考サイト

* [ExploreFlask - Organizing your project][ExploreFlask]
* [Modular Applications with Blueprints][Blueprints]
* [ExploreFlask - Blueprints][ExploreFlaskBlueprints]

[ExploreFlask]:http://exploreflask.com/en/latest/organizing.html
[Blueprints]:http://flask.pocoo.org/docs/0.12/blueprints/
[ExploreFlaskBlueprints]:http://exploreflask.com/en/latest/blueprints.html