【Flask講座 第2回】拡張性の高いWebアプリへ!Flaskの設計パターン入門

第1回で「Hello, World!」をブラウザに表示できた感動、素晴らしかったですね!Webサーバーを自分の手で動かしたという経験は、大きな自信に繋がったはずです。

しかし、今のrun.pyファイル1つだけの状態は、いわば「平屋の小さな小屋」のようなものです。これから私たちは、ユーザー登録機能、記事投稿機能、決済機能といった多くの「部屋」を増築していきます。もしこの小屋に無計画に部屋を足し続ければ、いずれはどこに何があるか分からない、修正も困難な「迷宮」になってしまうでしょう。

ご安心ください。この記事を読み終える頃には、あなたのアプリケーションは、将来どれだけ大きくなっても決して道に迷わない、プロ仕様の「高層ビル」の設計図を手に入れています。

本記事では、まず単一ファイル構成の危険性を学び、その解決策としてFlask開発における二大設計パターン「Application Factory」と「Blueprint」を導入。最後に、PythonコードとHTMLを美しく分離する方法を実践します。

この記事でわかること
  • 単一ファイルで開発を続けることのリスク
  • プロが使うFlaskの設計パターン「Application Factory」と「Blueprint」の概念
  • PythonのロジックとHTMLの見た目を分離する方法

前回の講座はこちらから

あわせて読みたい
【Flask講座 第1回】Webはどのように動くのか?開発環境を構築して最初のアプリを動かそう 学んだ知識を活かして、何か形になるものを作りたい ――今、あなたはそんな期待に胸を膨らませていることでしょう。その「何か」として、Webアプリケーション開発は最高...
目次

なぜ単一ファイルではダメなのか?

第1回で作成したrun.pyは、シンプルで分かりやすかったですよね。しかし、これは束の間の平和です。もし私たちがこのまま開発を続けたら、どのような未来が待っているのでしょうか?少しだけ時間を早送りして、その様子を覗いてみましょう。

忍び寄る「スパゲッティコード」の恐怖

第1回で作成したrun.pyは、シンプルでとても分かりやすいですよね。しかし、これは束の間の平和です。

もし、私たちがこのrun.pyファイル1つに、これから作る機能をすべて書き足していったらどうなるでしょうか?

私たちの「note風ブログサイト」には、これからユーザー登録、ログイン、記事の投稿、編集、コメント機能…といった、たくさんの機能が追加されていきます。これらの機能はすべて、@app.route()というルーティング処理を必要とします。

そもそも「ルーティング処理」って何のこと…?

なおくん

ルーティング処理とは、特定のURLへのアクセスを、どのPython関数が担当するかを振り分ける、交通整理のような仕組みのことです。
例えば、Flaskでは@app.route('/profile')と書くことで、「nao-kun.com/profile」にアクセスした際の処理を記述することができます。
自身のwebアプリのドメイン(https://nao-kun.com)のあとに「/~~~」でページを分けることができます。その処理をルーティング処理と呼び、Flaskでは@app.route('/~~~')と記述します。その下の関数名は何でも構いません「/」の後の文字に合わせる必要もありません。
自身のwebアプリにお問い合わせページを追加したいなら「@app.route(‘/contact’)」と書き関数名は「message」でも良いわけですね。

その結果、最初はたった数行だったrun.pyは、将来的に500行、1000行…と、どんどん長く、肥大化していきます。

ある日、あなたはログイン機能の小さなバグを修正する必要に迫られます。「さて、ログイン処理はどこに書いたかな…?」

あなたはその巨大なrun.pyを開き、スクロールを始めます。上から下へ、ひたすらスクロール、スクロール、スクロール…。自分自身が書いたはずのコードの海の中で、目的の関数を見つけるだけで一苦労です。これでは、あまりに非効率ですよね。

さらに恐ろしいのは、ある一部分を修正したことで、全く関係ないと思っていた別の機能が動かなくなる、といった現象が起き始めることです。コード同士が複雑に絡み合い、もはやどこをどう直せばいいのか分からない…。

これが、開発者たちが恐れる「スパゲッティコード」の正体です。


プロの設計思想①:Application Factory パターン

スパゲッティコード問題を解決するため、私たちはプロが使う最初の設計思想「Application Factory パターン」を導入します。一見すると少し遠回りに見えるかもしれませんが、これは、ほとんど全ての優れたFlaskアプリケーションの基礎となる、極めて重要な考え方です。

アプリケーションの「製造工場」を作る

第1回で、私たちはファイルの冒頭にこう書きました。

# run.py (第1回のコード)
app = Flask(__name__)

これは、Pythonがこのファイルを読み込んだ瞬間に、Webアプリケーションの本体(専門用語でインスタンスと言います)を作成する、最も直接的な方法です。

Application Factory パターンは、このアプローチを根本的に変えます。 アプリケーションのインスタンスを直接作る代わりに、アプリケーションを製造し、設定し、そして完成品として返却する役割だけを持つ、create_app()という名前の関数を作ります。

例えるなら、第1回の方法は「ガレージで一台の特注車を手作りする」ようなものです。対してApplication Factoryは「自動車工場を建設する」ようなもの。この工場(create_app関数)さえあれば、私たちはいつでも好きな時に、好きな仕様の車(appインスタンス)を何台でも生産できるようになるのです。

# Application Factoryパターンのサンプルコード

from flask import Flask

def create_app():
    # Flaskアプリケーションのインスタンスを作成
    app = Flask(__name__)

    # ここに、今後作成するアプリの登録や、
    # データベースの初期化などの設定を追加していく

    # サンプルのルーティング
    @app.route('/hello')
    def hello():
        return 'Hello, from the App Factory!'

    # 設定済みのアプリケーションインスタンスを返す
    return app

Application Factoryの3つのメリット

この「工場」を作るアプローチは、少しだけ余分な手間がかかるように見えますが、後々の開発であなたを大きな頭痛から救ってくれる、3つの絶大なメリットがあります。

1. テストの容易性 Webアプリケーションをテストする際には、本番用とは別の「テスト用データベース」を使うなど、特別な設定をしたアプリのインスタンスが必要になります。Application Factoryがあれば、create_app('testing')のように、テスト用の設定を渡してあげるだけで、テスト専用のアプリを簡単に、そして安全に作り出すことができます。

2. 柔軟な設定の切り替え テストと同様に、あなたの開発用PCで動かすアプリと、実際にインターネットに公開する本番サーバーで動かすアプリでは、設定を切り替えたい場面が多々あります(例: デバッグモードのON/OFFなど)。このパターンを使えば、異なる環境向けのアプリを同じ「工場」から柔軟に生産できます。

3. 「循環参照」エラーの回避 これは、アプリケーションが大きくなるにつれて開発者を悩ませる、非常に厄介なエラーです。

今はピンとこなくても大丈夫です

「循環参照」とは、ファイルAがファイルBの情報を必要とし、同時にファイルBもファイルAの情報を必要としてしまう、プログラムが「堂々巡り」に陥ってしまう状態です。アプリのインスタンスを最初に直接作ってしまうと、この問題が非常に起きやすくなります。

Application Factoryパターンは、アプリのインスタンスを関数の中で作ることで、この複雑で発見しにくいエラーを未然に防いでくれる効果があります。今は「将来のやっかいなエラーを防ぐための、プロのお作法なんだな」くらいに理解しておけば完璧です!

これらのメリットを享受するため、私たちは最初から、このプロフェッショナルな設計パターンを採用します。

プロの設計思想②:Blueprint パターン

アプリケーションの「製造工場」(create_app)はできましたが、この工場でこれから様々な製品(機能)を作っていくにあたり、製品の「設計図」をきちんと整理しておく必要があります。そのための第二の設計思想が「Blueprint (ブループリント)」です。

アプリケーションを「分社化」するBlueprint

私たちの「note風ブログサイト」が、将来的に巨大な企業グループ「NoteCloneホールディングス」に成長したと想像してみてください。

この大企業には「アカウント関連事業部」「記事投稿事業部」「決済サービス事業部」など、たくさんの部門があります。もし、これらの全部門が同じ一つのビル(一つのPythonファイル)で働いていたら、どうなるでしょうか?

きっと、組織は混乱し、非効率的になりますよね。

賢い経営者(CEO)は、事業部ごとに会社を分割、すなわち「分社化」を考えます。

  • 親会社(ホールディングス) = Flaskアプリケーション本体 (create_appで作られるapp)
  • 子会社 = Blueprint
  • 各子会社の事業内容 = ルーティング処理 (@app.route)

アカウント関連の機能は「株式会社NoteClone認証サービス」という子会社(Blueprint)に、記事投稿関連の機能は「株式会社NoteCloneパブリッシング」という子会社(Blueprint)に、それぞれ専門の会社として独立させるのです。

親会社はグループ全体の管理・統合に徹し、各子会社はそれぞれの専門分野に集中する。これにより、組織全体が非常にスッキリとし、見通しが良くなります。

Blueprintは、まさにこの「分社化」をFlaskアプリケーションで実現するための仕組みなのです。

Blueprintの具体的な役割

例え話はこれくらいにして、技術的な話をしましょう。 一言でいうと、Blueprintとは「関連するルーティング処理(関数)の集まりを、一つの部品(モジュール)として定義するための仕組み」です。

@app.route()という記述は、アプリケーション本体(app)に直接ルート(https://nao-kun.com/~~~)を登録していました。しかしBlueprintを使えば、アプリケーション本体とは独立した「設計図」として、一時的にルートを登録しておくことができます。

これにより、私たちは以下のような整理が可能になります。

  • ユーザー登録やログインに関するルートは auth_views.py にまとめる。
  • 記事の投稿や表示に関するルートは post_views.py にまとめる。
  • 決済に関するルートは payment_views.py にまとめる。

そして最後に、これらのBlueprint(子会社たち)を、Application Factory(create_app関数)の中でアプリケーション本体(親会社)に「登録」してあげることで、初めてアプリケーション全体の機能として統合されるのです。

このパターンを使うことで、私たちはファイル単位で機能を明確に分割でき、どこに何が書かれているのかが一目瞭然の、非常に管理しやすいプロジェクトを維持することができます。

実践!Hello Worldをリファクタリングしよう

理論を学んだところで、いよいよ実践です。第1回で作成したシンプルなrun.pyを、Application FactoryとBlueprintを使ったプロフェッショナルな構成にリファクタリング(再構築)していきましょう。

なおくん

リファクタリングとは、プログラムの動作自体は変更せず内部の処理の方法をより効率化することです。

新しいプロジェクト構成

まず、最終的に目指すフォルダとファイルの構成(ディレクトリツリー)を確認します。appという新しいフォルダを作成し、その中に機能ごとのファイルを配置していくのがポイントです。

flask-note-app/
├── venv/
├── app/
│   ├── __init__.py      #【新規】Application Factory(create_app)を定義します
│   ├── views/           #【新規】Blueprintを格納するフォルダです
│   │   ├── __init__.py  #【新規】内容は記述しない(作成するだけ)
│   │   └── main_views.py  #【新規】メイン機能のBlueprintを定義します
│   └── templates/       #【新規】HTMLファイルを格納するフォルダです
│       └── index.html     #【新規】トップページのHTMLファイルです
└── run.py               #【変更】サーバー起動用のシンプルなファイルになります
__init__.pyとは

__init__.pyファイルは、そのファイルが置かれているフォルダを、Pythonの「パッケージ」として扱えるようにするための特別なファイルです。

  • パッケージの目印 📦: Pythonは、あるフォルダの中に__init__.pyファイルを見つけると、「あ、このフォルダは単なるフォルダじゃなくて、ひとまとまりのPythonモジュールなんだな」と認識します。
  • 初期化コードの置き場所: そのパッケージが読み込まれた(importされた)時に、最初に実行したい処理をこのファイル内に書くことができます。今回のcreate_app関数のように、パッケージ全体の初期設定や準備を行うのに最適な場所です。

たとえるなら、__init__.py「おもちゃ箱のラベル兼組立説明書」のようなものです。このファイルがあることで、Pythonはただの箱(フォルダ)ではなく「ミニカーセット」という名前の付いたおもちゃ箱(パッケージ)だと認識でき、中身をどう扱えばいいか(初期化処理)を知ることができます。

以前のバージョンのPythonでは、このファイルが必須でしたが、現在では空の__init__.pyファイルがなくてもパッケージとして認識される場合もあります。しかし、パッケージの初期化処理を記述する場所として今でも広く使われており「このフォルダはPythonのパッケージですよ」と明示するための良い習慣とされています。

コードの分割と実装

それでは、上記の構成図に従って、各ファイルを作成・編集していきましょう。

1. Blueprintの作成 (app/views/main_views.py)

最初に「子会社」となるBlueprintを作成します。appフォルダの中にviewsというフォルダを、さらにその中にmain_views.pyというファイルを作成してください。

# app/views/main_views.py

# render_template: HTMLファイルをレンダリングする関数
# Blueprint: Blueprintを作成するクラス
from flask import Blueprint, render_template

# Blueprintオブジェクトを作成
# 'main'はBlueprintの名前、__name__はURL生成で必要
bp = Blueprint('main', __name__)

@bp.route('/')
def index():
    # templatesフォルダ内のindex.htmlをレンダリングする
    return render_template('index.html')

2. Application Factoryの作成 (app/__init__.py)

次に、「親会社」の役割を担うApplication Factoryを作成します。appフォルダの中に__init__.pyというファイルを作成してください。

# app/__init__.py

from flask import Flask

# 先ほど作成したmain_views.pyからbp(Blueprintオブジェクト)をインポート
from .views import main_views

def create_app():
    # Flaskアプリケーションのインスタンスを作成
    app = Flask(__name__)

    # 作成したBlueprintをアプリケーションに登録
    app.register_blueprint(main_views.bp)

    # 設定済みのアプリケーションインスタンスを返す
    return app
ポイント

from .views import main_viewsの行で、先ほど作った「子会社」(main_views.py)を読み込み、app.register_blueprint()で「親会社」に正式に登録しています。

3. 起動ファイルの修正 (run.py)

最後に、プロジェクトのルートにあるrun.pyを、Application Factoryを呼び出すだけのシンプルな内容に書き換えます。

# run.py

# appフォルダの__init__.pyからcreate_app関数をインポート
from app import create_app

# create_app関数を実行して、アプリケーションインスタンスを作成
app = create_app()

# このファイルが直接実行された場合にのみ、開発サーバーを起動
if __name__ == '__main__':
    app.run(debug=True)
if name == ‘main‘: とは?

これは、Pythonファイルが「直接実行された場合のみ、中のコードを実行せよ」というおまじないです。この講座では主にflask runコマンドでサーバーを起動しますが、この記述はPythonスクリプトの標準的な作法として覚えておくと良いでしょう。

app.run(debug=True) にある debug=True って何ですか?

なおくん

debug=Trueは開発中にだけ使う、2つの便利な機能を持った「デバッグモード」を有効にするスイッチです。

自動リロード機能:デバッグモードがONだと、ソースコードを修正して保存するたびに、Webサーバーが自動で再起動して変更を反映してくれます。これをOFFにしていると、コードを修正するたびに手動でサーバーを止めたり起動したりする必要があり、とても面倒です。
インタラクティブデバッガー:もしコードにエラーが発生した場合、ブラウザ上に非常に詳細なエラー情報が表示されるようになります。どのファイルの何行目でエラーが起きたのか、その時の変数の値はどうだったのか、といった情報が分かるため、バグの原因を特定するのが格段に楽になります。
ただし、エラーの詳細情報が外部に見えてしまうのはセキュリティ上とても危険なので、この設定は開発中限定です。絶対に本番環境ではONにしないでくださいね!

HTMLを分離する

最後に、PythonコードからHTMLを分離します。これは「関心の分離」という非常に重要な設計原則です。

一流のシェフ(Python)は、最高の料理を作ることに集中すべきで、お皿のデザイン(HTML)まで気にする必要はない。

この原則に従いapp/templatesフォルダの中にindex.htmlファイルを作成し、以下の内容を記述します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Flask Note App</title>
</head>
<body>
    <h1>Hello, World! from a Template!</h1>
    <p>プロジェクトの構造化に成功しました!</p>
</body>
</html>
templatesフォルダについて

Flaskでは「templates」というフォルダ内にHTMLファイルを格納します。これはFlaskの初期設定で決まっており、もし仮に「templates」というフォルダ名以外のフォルダ名にしたい場合は、このように指定することで「templates」以外のフォルダ名にすることが可能ですが、実際には別名に変更する理由が特にないので、特別な事情がない限りは「templates」というフォルダ名を作成して、HTMLを格納しましょう。

app = Flask(__name__, template_folder='任意のフォルダ名')

これでリファクタリングは完了です! ターミナルで、第1回と同じようにset FLASK_APP=run.py (macOS: export FLASK_APP=run.py) を設定し、flask runを実行してみてください。

なおくん

ターミナルソフトでなくても、VScodeなどエディタに付属のターミナルでも問題ありません。
VScodeの場合で日本語WindowsキーボードならCtrl + @・英語WindowsキーボードならCtrl + Shift + `で、Macの場合はCommand + Jで起動することができます。

ブラウザでhttp://127.0.0.1:5000にアクセスすると、新しいHTMLの内容が表示されるはずです。見た目は似ていますが、その裏側の構造は、プロフェッショナルなアプリケーションへと大きく進化しました。

つまずきポイント解説

この第2回で導入した設計パターンは、将来の開発を非常にスムーズにしてくれますが、最初のうちは少し戸惑うかもしれません。ここで、想定されるつまずきポイントを先に見ていきましょう。

エラー: TemplateNotFound (テンプレートが見つかりません)

表示されるエラー: jinja2.exceptions.TemplateNotFound: index.html

  • 原因: render_template('index.html')を実行した際、Flaskがindex.htmlファイルを見つけられずにいます。これは、Flaskが探すべきtemplatesという名前のフォルダが存在しないか、index.htmlがその中に入っていないことが原因です。
  • 対策: Application Factory(create_app関数)を定義しているapp/__init__.pyファイルと同じ階層に、必ずtemplatesという名前(templateのように複数形のsを忘れないでください)のフォルダを作成し、その中にindex.htmlを配置してください。Flaskはこのフォルダ構成を規約としています。
app/
├── __init__.py      # create_appはここ
└── templates/       # このフォルダが必要
    └── index.html
エラー: Blueprintで定義したページが404 Not Foundになる

表示されるエラー: ブラウザにNot Foundと表示され、開発サーバーのログにも404と記録される。

  • 原因: Blueprintを定義しただけでは、アプリケーションはその存在を知りません。作成したBlueprintを、Application Factoryの中でアプリケーション本体に「登録」する作業が漏れていることが原因です。
  • 対策: app/__init__.pyの中にあるcreate_app関数で、作成したBlueprintをインポートし、app.register_blueprint()を使って必ず登録してください。「大企業の分社化」の例えで言えば、「子会社を作っただけではダメで、親会社にグループ企業として登記しないといけない」のと同じです。
# app/__init__.py
from flask import Flask
from .views import main_views # Blueprintをインポート

def create_app():
    app = Flask(__name__)

    app.register_blueprint(main_views.bp) # この行が重要!

    return app
エラー: ImportError: attempted relative import with no known parent package

表示されるエラー: run.pyを実行しようとすると、上記のようなインポートエラーが表示される。

  • 原因: プロジェクトのルートディレクトリからではなく、appディレクトリの中に入ってからpython __init__.pyなどを実行しようとした場合に発生しやすいエラーです。Pythonがプロジェクトの全体構造を正しく認識できていません。
  • 対策: Flaskアプリケーションの実行やflaskコマンドは、必ずプロジェクトのルートディレクトリ(この講座ではflask-note-app)で行うようにしてください。ターミナルでcdコマンドを使い、正しい場所にいることを確認してからコマンドを実行する習慣をつけましょう。

まとめ:拡張可能なアプリの礎を築く

お疲れ様でした!今回の内容は、少し抽象的で難しく感じたかもしれませんが、あなたは非常に重要なステップを乗り越えました。

  • 単一ファイルで開発を続けることの危険性(スパゲッティコード)を学びました。
  • プロの設計思想である「Application Factory」パターンで、アプリの製造工場を作りました。
  • Blueprint」パターンを使い、機能を「分社化」して整理する手法を学びました。
  • render_templateを使い、PythonのロジックとHTMLの見た目を美しく分離しました。

ブラウザに表示される結果は「Hello, World!」のままで、見た目の変化はありません。しかし、水面下ではあなたのアプリケーションは劇的な進化を遂げました。これで、私たちは今後追加される多くの機能を、混乱なく、整理された状態で開発していくことができます。

重要なマイルストーン!

あなたは、今後の開発をスムーズに進めるための、拡張性の高い「設計図」を手に入れました。これは、この講座における非常に重要なマイルストーンです。

次回は、この強固な土台の上に、アプリケーションの「記憶」を司るデータベースを接続していきます。いよいよ、私たちのアプリがデータを持ち、より動的に振る舞うようになります。どうぞお楽しみに!

目次