【初心者向け】pip install flask で始めるFlask入門

【初心者向け】pip install flask で始めるFlask入門

ウェブアプリケーション開発に興味があるけれど、何から始めて良いか分からない――そんな方にとって、PythonのマイクロフレームワークであるFlaskは、最初のステップとして非常に適しています。Flaskはシンプルで軽量でありながら、強力なウェブアプリケーションを構築するための基本的な機能を提供します。

この記事では、「pip install flask」というコマンド一つから始め、Flaskを使って最小限のウェブアプリケーションを構築し、さらにルーティング、テンプレート、データベース連携といった基本的な要素を学んでいきます。最終的には、Flaskで簡単なウェブサイトを作成できるようになることを目指します。

Pythonの基本的な文法を理解している方を対象としていますが、ウェブ開発の経験は問いません。さあ、一緒にFlaskの世界へ飛び込みましょう!

1. はじめに:Flaskとは何か? なぜFlaskを学ぶのか?

1.1 Flaskとは?

Flaskは、Pythonで書かれたマイクロウェブフレームワークです。「マイクロフレームワーク」と呼ばれるのは、ウェブアプリケーション開発に必要な最低限の機能(ルーティング、リクエスト処理など)のみを提供し、データベースアクセスやフォーム検証などの高度な機能は拡張機能(Extensions)として提供されるためです。これにより、開発者は必要な機能だけを選んで利用でき、アプリケーションの構造を自由に設計できます。

Flaskは以下の主要なコンポーネントで構成されています:

  • Werkzeug: WSGI(Web Server Gateway Interface)ユーティリティライブラリ。ウェブサーバーとPythonアプリケーションの間で通信するためのインターフェースを提供します。リクエストオブジェクトやレスポンスオブジェクトの処理などを担当します。
  • Jinja2: モダンでデザイナブルなテンプレートエンジン。HTMLファイルの中にPythonのコードのようなものを記述し、動的にウェブページを生成するために使用されます。
  • MarkupSafe: HTMLエスケープのためのライブラリ。クロスサイトスクリプティング(XSS)などのセキュリティ脆弱性を防ぐのに役立ちます。
  • ItsDangerous: セキュアなシリアライゼーションをサポートするライブラリ。セッション管理などに使用されます。
  • Click: コマンドラインインタフェース作成ライブラリ。Flaskのコマンドラインツール(flask runなど)で使用されています。

pip install Flask コマンドを実行すると、これらの依存関係も一緒にインストールされます。

1.2 なぜFlaskを学ぶのか?

  • シンプルで理解しやすい: コード量が少なく、構造が明確なため、ウェブフレームワークの仕組みを理解するのに適しています。初心者でも比較的短時間で動くものを作れます。
  • 柔軟性が高い: マイクロフレームワークであるため、特定の技術やツールに縛られず、開発者が自由にアーキテクチャを選択できます。小規模なスクリプトから大規模なアプリケーションまで対応可能です。
  • 豊富な拡張機能: Flaskエコシステムには、データベース連携(Flask-SQLAlchemy)、ユーザー認証(Flask-Login)、RESTful API構築(Flask-RESTful)など、様々な機能を提供する拡張機能が豊富に存在します。必要な機能は後から追加できます。
  • Pythonコミュニティの強み: Pythonは世界中で広く使われている言語であり、Flaskも活発なコミュニティを持っています。困ったときに情報を得やすく、多くのライブラリを利用できます。
  • 学習コストが低い: Djangoのようなフルスタックフレームワークに比べて、学習すべき概念が少ないため、手軽に始められます。

Flaskは、小規模なAPI、個人ブログ、プロトタイプの開発など、様々な用途で利用されています。また、ウェブフレームワークの基本的な概念(ルーティング、HTTPメソッド、テンプレート、リクエスト/レスポンスサイクルなど)を学ぶための教材としても優れています。

この記事では、Flaskの基本的な機能に焦点を当て、一歩ずつ理解を深めていきます。

2. 開発環境の準備

Flaskを使った開発を始める前に、いくつかの準備が必要です。

2.1 Pythonのインストール

FlaskはPythonで動作します。まだPythonがインストールされていない場合は、公式ウェブサイトからインストーラーをダウンロードしてインストールしてください。多くのOS(Windows, macOS, Linux)には最初からインストールされている場合もありますが、最新版を利用することをおすすめします。

インストールの確認は、ターミナル(コマンドプロンプトやPowerShell)を開いて以下のコマンドを実行します。

“`bash
python –version

または

python3 –version
“`

Pythonのバージョン情報が表示されれば成功です。もし表示されない場合や古いバージョンの場合は、最新版をインストールしてください。この記事ではPython 3.6以降を想定しています。

2.2 仮想環境の重要性

ウェブアプリケーション開発では、プロジェクトごとに異なるライブラリのバージョンが必要になることがよくあります。例えば、あるプロジェクトではFlask v1.1を使い、別のプロジェクトではFlask v2.0を使いたい、といったケースです。もし全てのライブラリをシステム全体にインストールしてしまうと、バージョンの衝突や依存関係の問題が発生しやすくなります。

これを避けるために、「仮想環境(Virtual Environment)」を作成するのが一般的です。仮想環境は、プロジェクトごとに独立したPython環境とライブラリのインストール場所を提供します。これにより、プロジェクト間で依存関係が影響し合うのを防ぐことができます。

Python 3.3以降では、標準ライブラリとして venv (virtual environment) という仮想環境を作成・管理するツールが提供されています。これを利用するのが最も手軽です。

2.3 仮想環境の作成とアクティベート

まずは、プロジェクトを置くディレクトリを作成し、その中に移動します。

bash
mkdir myflaskapp
cd myflaskapp

次に、このディレクトリ内に仮想環境を作成します。仮想環境の名前は任意ですが、.venvvenv とすることが多いです。ここでは venv とします。

“`bash
python -m venv venv

または python3 -m venv venv

“`

このコマンドを実行すると、myflaskapp ディレクトリの中に venv というディレクトリが作成されます。これがあなたの仮想環境のホームディレクトリになります。

仮想環境を作成したら、それを使用するために「アクティベート(有効化)」する必要があります。アクティベートすることで、以降の python コマンドや pip コマンドはその仮想環境内のものを指すようになります。

OSによってアクティベートの方法が異なります。

macOS / Linux:

bash
source venv/bin/activate

Windows (コマンドプロンプト):

bash
venv\Scripts\activate.bat

Windows (PowerShell):

powershell
venv\Scripts\Activate.ps1

アクティベートに成功すると、ターミナルのプロンプトの先頭に仮想環境の名前(例: (venv))が表示されるようになります。

bash
(venv) user@host:~/myflaskapp$

これで、このターミナルセッションでは仮想環境が有効になっています。ここでインストールしたライブラリはこの仮想環境内にのみ存在し、他のPython環境には影響しません。

仮想環境から退出するには、以下のコマンドを実行します。

bash
deactivate

プロンプトから (venv) が消えれば、仮想環境から退出したことになります。開発を再開する際は、再びプロジェクトディレクトリで上記の activate コマンドを実行してください。

この仮想環境をアクティベートした状態で、次のステップであるFlaskのインストールを行います。

3. Flaskのインストール

仮想環境がアクティベートされていることを確認したら、いよいよFlaskをインストールします。コマンドは非常にシンプルです。

bash
(venv) user@host:~/myflaskapp$ pip install Flask

このコマンドを実行すると、pip というパッケージマネージャーが、Python Package Index (PyPI) からFlaskパッケージをダウンロードし、現在アクティベートされている仮想環境にインストールします。Flaskだけでなく、その依存関係であるWerkzeug、Jinja2なども自動的にインストールされます。

インストールが完了すると、以下のようなメッセージが表示されるはずです。(バージョン番号は実行時の最新版によって異なります)

Collecting Flask
Downloading Flask-2.2.2-py3-none-any.whl (101 kB)
Collecting Werkzeug>=2.2.2
Downloading Werkzeug-2.2.2-py3-none-any.whl (232 kB)
Collecting Jinja2>=3.0
Downloading Jinja2-3.1.2-py3-none-any.whl (133 kB)
Collecting itsdangerous>=2.0
Downloading itsdangerous-2.1.2-py3-none-any.whl (15 kB)
Collecting Click>=8.0
Downloading click-8.1.3-py3-none-any.whl (96 kB)
Installing collected packages: Werkzeug, Jinja2, itsdangerous, Click, Flask
Successfully installed Click-8.1.3 Flask-2.2.2 itsdangerous-2.1.2 Jinja2-3.1.2 Werkzeug-2.2.2

インストールされたパッケージを確認するには、以下のコマンドを実行します。

bash
(venv) user@host:~/myflaskapp$ pip list

インストールされたライブラリのリストが表示され、その中に FlaskWerkzeugJinja2 などが含まれていることを確認できます。

これで、Flaskアプリケーションを開発するための準備が整いました!

4. 最小のFlaskアプリケーション

Flaskのインストールが完了したので、早速動かしてみましょう。最小限のFlaskアプリケーションは、数行のコードで作成できます。

プロジェクトディレクトリ (myflaskapp) の中に、例えば app.py という名前でファイルを作成します。

“`python

app.py

from flask import Flask

app = Flask(name)

@app.route(‘/’)
def index():
return ‘Hello, Flask!’

if name == ‘main‘:
app.run(debug=True)
“`

このコードの各部分を説明します。

  • from flask import Flask: Flaskクラスをインポートします。これを使ってアプリケーションインスタンスを作成します。
  • app = Flask(__name__): Flaskアプリケーションのインスタンスを作成します。__name__ は、現在のモジュールの名前をFlaskに知らせるための引数です。これにより、Flaskはテンプレートファイルや静的ファイルを探す際に、どこから始めるべきかを知ることができます。特別な理由がない限り、この引数には __name__ を渡すのが慣習です。
  • @app.route('/'): これはデコレーターと呼ばれるPythonの機能です。Flaskにおいて、URLパス (/) とそのパスへのリクエストを処理する関数 (index) を関連付ける役割を果たします。この例では、サイトのルートURL(例えば http://127.0.0.1:5000/)にアクセスがあったときに、その直下の index() 関数が実行されるように設定しています。
  • def index():: この関数は、先ほどのデコレーター @app.route('/') によって指定されたURLにアクセスがあったときに実行されます。
  • return 'Hello, Flask!': 関数が実行された結果として、クライアント(ブラウザなど)に返されるレスポンスの内容です。ここでは単純な文字列を返しています。ウェブブラウザはこれをHTMLとして解釈し、ページに「Hello, Flask!」と表示します。
  • if __name__ == '__main__':: これはPythonの標準的な記述です。このスクリプトが直接実行された場合にのみ、ブロック内のコードが実行されます。インポートされた場合は実行されません。
  • app.run(debug=True): Flaskアプリケーションの開発サーバーを起動します。debug=True はデバッグモードを有効にするオプションです。デバッグモードでは、コードの変更を検知してサーバーが自動的に再起動されたり、エラーが発生した際に詳細なデバッグ情報がブラウザに表示されたりするため、開発効率が向上します。ただし、デバッグモードはセキュリティ上のリスクがあるため、本番環境では絶対に有効にしないでください

4.1 アプリケーションの実行

app.py ファイルを保存したら、仮想環境がアクティベートされているターミナルで以下のコマンドを実行します。

bash
(venv) user@host:~/myflaskapp$ python app.py

または、FlaskのCLI (Command Line Interface) を利用することもできます。

bash
(venv) user@host:~/myflaskapp$ flask run

flask run を使用する場合、デフォルトでは app.py または wsgi.py ファイル内のアプリケーションインスタンス(変数名が app または application のもの)を探して実行します。ファイル名やアプリケーションインスタンス名が異なる場合は、環境変数 FLASK_APP を設定する必要がありますが、ここでは app.pyapp インスタンスなので、そのまま実行できます。

どちらのコマンドを実行しても、以下のような出力が表示されるはずです。

(venv) user@host:~/myflaskapp$ python app.py
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: XXX-XXX-XXX

これはFlaskの開発サーバーが起動し、http://127.0.0.1:5000/ でリクエストを待ち受けていることを示しています。

4.2 ブラウザでの確認

ウェブブラウザを開き、アドレスバーに http://127.0.0.1:5000/ と入力してアクセスしてみてください。

ブラウザの画面に「Hello, Flask!」と表示されれば成功です!

ターミナルを見ると、アクセスがあったログが表示されているはずです。

127.0.0.1 - - [01/Jan/2023 10:00:00] "GET / HTTP/1.1" 200 -

これは、ローカルホスト (127.0.0.1) から GET メソッドで / パスへのリクエストがあり、サーバーが 200 OK のステータスコードで応答したことを示しています。

サーバーを停止するには、ターミナルで Ctrl+C を押します。

これで、Flaskを使った最も基本的なウェブアプリケーションを作成し、実行することができました。

5. ルーティング

ウェブアプリケーションにおいて、ルーティングは非常に重要な概念です。ルーティングとは、ユーザーがブラウザでアクセスしたURL(例えば /about, /users/123 など)に応じて、サーバー側でどのPython関数を実行するかを決定する仕組みのことです。

Flaskでは、@app.route() デコレーターを使ってルーティングを設定します。

5.1 基本的なルーティング

先ほどの app.py に、新しいページへのルーティングを追加してみましょう。

“`python

app.py

from flask import Flask

app = Flask(name)

@app.route(‘/’)
def index():
return ‘Hello, Flask!’

/about というパスへのルーティングを追加

@app.route(‘/about’)
def about():
return ‘これはAboutページです。’

if name == ‘main‘:
app.run(debug=True)
“`

サーバーを再起動し (python app.py または flask run)、ブラウザで http://127.0.0.1:5000/about にアクセスしてみてください。

「これはAboutページです。」と表示されるはずです。

このように、@app.route('/path') デコレーターを使えば、簡単に新しいページ(エンドポイント)を追加できます。

5.2 URL変数

ウェブアプリケーションでは、URLの一部として情報を受け取りたい場合があります。例えば、ユーザーのプロフィールページ /users/username や、記事の詳細ページ /articles/article_id のように、URL自体が特定の項目を示している場合です。

Flaskでは、URLのパスの一部を変数として扱うことができます。変数の部分を <variable_name> の形式で記述します。

“`python

app.py

from flask import Flask

app = Flask(name)

@app.route(‘/’)
def index():
return ‘Hello, Flask!’

@app.route(‘/about’)
def about():
return ‘これはAboutページです。’

の部分が変数として関数に渡される

@app.route(‘/user/‘)
def show_user_profile(username):
# username 変数を使って動的なレスポンスを生成
return f’ユーザー名: {username}’

if name == ‘main‘:
app.run(debug=True)
“`

サーバーを再起動し、ブラウザで http://127.0.0.1:5000/user/Alicehttp://127.0.0.1:5000/user/Bob にアクセスしてみてください。

それぞれのユーザー名に応じたメッセージが表示されるはずです。<username> の部分に指定した値が、関数の引数 username として渡されていることがわかります。

5.3 URL変数の型変換

URL変数には、デフォルトでは文字列として値が渡されますが、特定の型に変換することも可能です。これは、例えばIDを指定する際に整数として扱いたい場合などに便利です。

型を指定するには、<converter:variable_name> の形式で記述します。Flaskで利用できる主なコンバーターは以下の通りです。

  • string (デフォルト): 任意の文字列 (スラッシュを除く)
  • int: 正の整数
  • float: 正の浮動小数点数
  • path: スラッシュを含む任意の文字列
  • uuid: UUID文字列

例として、記事IDを整数で受け取るルーティングを考えます。

“`python

app.py

from flask import Flask

app = Flask(name)

… (前のコードは省略) …

の部分が整数として関数に渡される

@app.route(‘/article/‘)
def show_article(article_id):
# article_id はここでは整数型
return f’記事ID: {article_id}’

の部分がスラッシュを含む文字列として渡される

@app.route(‘/path/‘)
def show_subpath(subpath):
return f’サブパス: {subpath}’

if name == ‘main‘:
app.run(debug=True)
“`

サーバーを再起動し、以下のURLにアクセスしてみてください。

  • http://127.0.0.1:5000/article/123 -> 「記事ID: 123」と表示 (123は整数として渡される)
  • http://127.0.0.1:5000/article/abc -> Not Found エラー (abcは整数に変換できないため)
  • http://127.0.0.1:5000/path/a/b/c -> 「サブパス: a/b/c」と表示

このように、URL変数の型を指定することで、より厳密なルーティングやデータのハンドリングが可能になります。

5.4 HTTPメソッドの指定

ウェブアプリケーションでは、同じURLパスでも、リクエストのHTTPメソッド(GET, POST, PUT, DELETEなど)によって異なる処理を行いたい場合があります。例えば、商品一覧ページはGETリクエストで表示し、新しい商品を登録する処理はPOSTリクエストで行う、といったケースです。

Flaskの @app.route() デコレーターでは、methods 引数を使って許可するHTTPメソッドを指定できます。デフォルトは ['GET'] です。

例として、簡単なフォームを作成し、GETで表示、POSTでデータを受け取る処理を考えてみましょう。

“`python

app.py

from flask import Flask, request # request オブジェクトをインポート

app = Flask(name)

… (前のコードは省略) …

/login パスに対して GET と POST メソッドを許可

@app.route(‘/login’, methods=[‘GET’, ‘POST’])
def login():
# リクエストのメソッドを判定
if request.method == ‘POST’:
# POST リクエストの場合、フォームデータを取得
# request.form は辞書のようなオブジェクトで、フォームデータのキーを指定して値を取得できる
username = request.form.get(‘username’) # name=”username” の input 要素の値を取得
password = request.form.get(‘password’) # name=”password” の input 要素の値を取得
# ここで認証処理などを行う(今回は省略)
return f’ユーザー名: {username}, パスワード: {password} でログイン試行’
else:
# GET リクエストの場合、ログインフォームを表示(テンプレートは後述)
# とりあえず簡易的なHTMLを返す
return ”’

”’

if name == ‘main‘:
app.run(debug=True)
“`

サーバーを再起動し、http://127.0.0.1:5000/login にアクセスすると、簡単なログインフォームが表示されます。

フォームにユーザー名とパスワードを入力して「ログイン」ボタンをクリックすると、フォームデータがPOSTリクエストとして /login パスに送信され、サーバー側で request.method == 'POST' のブロックが実行され、入力された値を含むメッセージが表示されます。

ここではフォームのHTMLを直接文字列として返していますが、実際のアプリケーションではテンプレートエンジンを使ってHTMLを生成するのが一般的です。次のセクションで詳しく説明します。

6. テンプレートエンジン (Jinja2)

前節のログインフォームのように、Pythonコード内でHTMLを文字列として記述するのは、見た目が分かりにくく、複雑なHTMLになると管理が非常に困難になります。また、HTMLの一部を動的に変更したい場合(例えば、ユーザー名を表示したり、データのリストを表示したり)も、文字列操作では限界があります。

このような問題を解決するのが「テンプレートエンジン」です。テンプレートエンジンは、HTMLファイルの中に特別な記法でプレースホルダーや制御構造(条件分岐、ループなど)を記述し、実行時にサーバー側のデータを使ってそれらを置き換えることで、動的にHTMLを生成します。

FlaskはデフォルトでJinja2という強力なテンプレートエンジンを使用します。

6.1 なぜテンプレートが必要か?

  • コードとデザインの分離: PythonコードとHTMLマークアップを分離できるため、それぞれの修正が容易になります。
  • 動的なコンテンツ生成: サーバーから渡されたデータを使って、HTMLの一部を動的に生成できます(例: ユーザー名、記事一覧、フォームのエラーメッセージ)。
  • コードの再利用: ヘッダー、フッター、ナビゲーションバーなどの共通部分をテンプレートとして定義し、複数のページで再利用できます。
  • 可読性の向上: HTML構造の中にPythonのようなロジックを埋め込むことで、コード全体の流れが分かりやすくなります。

6.2 Jinja2の基本的な構文

Jinja2テンプレートでは、主に以下の構文を使用します。

  • 変数表示: {{ variable }}
    • Pythonコードから渡された変数の値をHTMLに出力します。値は自動的にエスケープされるため、セキュリティ上のリスク(XSSなど)を軽減できます。
  • 制御構造: {% ... %}
    • 条件分岐 (if/elif/else/endif) やループ (for/endfor) などのロジックを記述します。
  • コメント: {# ... #}
    • テンプレート内でのコメントです。最終的なHTML出力には含まれません。

6.3 テンプレートファイルの作成

Flaskアプリケーションでテンプレートを使用するには、アプリケーションインスタンスが置かれているディレクトリ(app.py がある場所)と同じ階層に templates という名前のディレクトリを作成し、その中にHTMLファイルを置くのが慣習です。

myflaskapp/
├── venv/
├── app.py
└── templates/
└── index.html # テンプレートファイル

templates ディレクトリの中に、index.html というファイルを作成し、以下の内容を記述してみましょう。

“`html






{{ page_title }}

{{ greeting }}

ここはトップページです。

果物リスト

    {% for fruit in fruits %}

  • {{ fruit }}
  • {% endfor %}

{% if show_extra_info %}

追加情報が表示されています。

{% endif %}

{# これはコメントです #}


“`

このテンプレートには、変数表示 {{ ... }} や制御構造 {% ... %} が含まれています。これらの部分は、Flask側から渡されるデータによって置き換えられます。

6.4 render_template 関数の使い方

Flaskでテンプレートをレンダリングするには、flask モジュールから render_template 関数をインポートして使用します。この関数は、指定されたテンプレートファイルを読み込み、渡された変数を使ってテンプレート内のプレースホルダーを置き換え、最終的なHTML文字列を生成します。

app.py を修正して、トップページでこのテンプレートをレンダリングするように変更します。

“`python

app.py

from flask import Flask, render_template # render_template をインポート

app = Flask(name)

@app.route(‘/’)
def index():
# テンプレートに渡すデータを辞書で定義
data = {
‘page_title’: ‘トップページ’,
‘greeting’: ‘ようこそ!’,
‘fruits’: [‘りんご’, ‘バナナ’, ‘オレンジ’],
‘show_extra_info’: True
}
# templates/index.html をレンダリングし、data 辞書のキーをテンプレート変数として渡す
return render_template(‘index.html’, data) # data は辞書を展開してキーワード引数として渡す

他のルーティング関数は省略…

if name == ‘main‘:
app.run(debug=True)
“`

サーバーを再起動し、http://127.0.0.1:5000/ にアクセスしてみてください。

テンプレートファイルの内容に基づいたHTMLが生成され、ブラウザに表示されるはずです。

  • {{ page_title }}'トップページ' に置き換わります。
  • {{ greeting }}'ようこそ!' に置き換わります。
  • {% for fruit in fruits %} ループによって、fruits リストの各要素が表示されます。
  • {% if show_extra_info %} 条件分岐によって、show_extra_infoTrue なので「追加情報が表示されています。」という段落が表示されます。

render_template('index.html', page_title='トップページ', greeting='ようこそ!', ...) のようにキーワード引数で変数を渡すこともできますが、辞書を使って **data のように渡す方が、変数が多い場合にコードがすっきりします。

6.5 テンプレート継承

多くのウェブサイトでは、ヘッダー、フッター、サイドバーなど、複数のページで共通して表示される部分があります。これらの共通部分をすべてのHTMLファイルに記述するのは非効率的です。

Jinja2では「テンプレート継承」という機能を使って、共通レイアウトを定義し、個別のページでそのレイアウトを「継承」して使用できます。これにより、コードの重複を減らし、保守性を向上させることができます。

テンプレート継承では、以下の構文を使用します。

  • {% extends 'base.html' %}: このテンプレートが base.html という別のテンプレートを継承することを宣言します。通常、テンプレートファイルの先頭に記述します。
  • {% block block_name %}{% endblock %}: 親テンプレートで定義された「ブロック」を、子テンプレートで上書き(または追加)します。ブロックは、子テンプレートが具体的なコンテンツを埋め込むための領域を定義します。

まず、共通レイアウトとなる「ベーステンプレート」を作成します。templates ディレクトリに base.html というファイルを作成します。

“`html






{% block title %}My Flask App{% endblock %}
{# ここに共通のCSSリンクなどを記述 #}

My Flask App


{% block content %}
{# 子テンプレートがこのブロックを上書きする #}

コンテンツがありません。

{% endblock %}

© 2023 My Flask App


“`

このベーステンプレートには、{% block title %}{% block content %} という2つのブロックが定義されています。title ブロックにはデフォルト値「My Flask App」が設定されています。

次に、このベーステンプレートを継承する子テンプレートを作成します。templates ディレクトリに index.html を修正(または新しく作成)します。

“`html

{% extends ‘base.html’ %} {# base.html を継承する #}

{% block title %}ホーム – {{ super() }}{% endblock %} {# titleブロックを上書き #}

{% block content %} {# contentブロックを上書き #}

ホームへようこそ!

これはベーステンプレートを継承して作られたページです。

{# ここにindexページ固有のコンテンツを記述 #}
<h2>果物リスト</h2>
<ul>
{% for fruit in fruits %}
    <li>{{ fruit }}</li>
{% endfor %}
</ul>

{% endblock %}
“`

index.html では、{% extends 'base.html' %} で継承を宣言し、{% block title %}{% block content %} で親テンプレートの同名のブロックを上書きしています。{{ super() }} は、親テンプレートのブロックの内容を呼び出すためのJinja2の機能です。ここでは、親の title ブロックのデフォルト値「My Flask App」に「ホーム – 」を付け加えています。

最後に、app.pyindex 関数で、修正した index.html テンプレートをレンダリングします。

“`python

app.py

from flask import Flask, render_template, url_for # url_for もインポート

app = Flask(name)

url_for を使うために SECRET_KEY を設定 (セッションとは関係ないが、url_for の一部機能で内部的に使われることがあるため慣習的に設定)

app.config[‘SECRET_KEY’] = ‘your_secret_key_here’ # セキュアなランダムな文字列に変更すること

@app.route(‘/’)
def index():
data = {
‘fruits’: [‘りんご’, ‘バナナ’, ‘オレンジ’]
}
return render_template(‘index.html’, **data)

@app.route(‘/about’)
def about():
# about ページ用のテンプレートも作成し、base.html を継承させる
return render_template(‘about.html’) # about.html は別途作成が必要

… (他のルーティングは省略) …

if name == ‘main‘:
app.run(debug=True)
“`

about.html も作成します。

“`html

{% extends ‘base.html’ %}

{% block title %}About – {{ super() }}{% endblock %}

{% block content %}

このサイトについて

これはFlaskの入門記事のために作成されたサンプルアプリケーションです。

{% endblock %}
“`

サーバーを再起動し、http://127.0.0.1:5000/http://127.0.0.1:5000/about にアクセスしてみてください。

どちらのページも base.html のレイアウト(ヘッダー、フッターなど)を持ちつつ、それぞれのテンプレートファイル (index.html, about.html) で定義されたコンテンツブロックが表示されるはずです。

また、base.html のナビゲーションリンクで {{ url_for('index') }}{{ url_for('about') }} と記述している点に注目してください。url_for() 関数は、指定された関数名(エンドポイント名、ここではルーティング関数名)に対応するURLを動的に生成します。これは、URLを変更した場合でもテンプレートファイルを修正する必要がなくなり、メンテナンス性を向上させるのに役立ちます。url_for を利用するには、Flaskアプリケーションに SECRET_KEY の設定が必要になる場合があります(内部的な署名処理などで使用されることがあるため)。ここでは単純な文字列を設定していますが、実際にはランダムでセキュアな文字列を使用してください。

テンプレート継承をマスターすることで、効率的で管理しやすいウェブサイト構造を構築できます。

7. 静的ファイル

ウェブサイトでは、HTMLファイル以外にも、CSSファイル(スタイルシート)、JavaScriptファイル(クライアントサイドスクリプト)、画像ファイル、フォントファイルなど、サーバー側で動的に生成する必要のないファイルが多数必要になります。これらは「静的ファイル(Static Files)」と呼ばれます。

Flaskでは、デフォルトで静的ファイルを扱うための仕組みが用意されています。

7.1 static ディレクトリの利用

Flaskアプリケーションインスタンスが置かれているディレクトリ(app.py がある場所)と同じ階層に、static という名前のディレクトリを作成し、その中に静的ファイルを置くのが慣習です。

myflaskapp/
├── venv/
├── app.py
├── templates/
│ ├── base.html
│ ├── index.html
│ └── about.html
└── static/ # 静的ファイル用ディレクトリ
├── css/
│ └── style.css
├── images/
│ └── logo.png
└── js/
└── script.js

例として、static/css ディレクトリに style.css というCSSファイルを作成してみましょう。

“`css
/ static/css/style.css /
body {
font-family: sans-serif;
margin: 20px;
background-color: #f4f4f4;
}

header {
background-color: #333;
color: white;
padding: 10px 0;
text-align: center;
}

nav a {
color: white;
margin: 0 10px;
text-decoration: none;
}

main {
margin-top: 20px;
padding: 15px;
background-color: #fff;
border-radius: 8px;
}

footer {
text-align: center;
margin-top: 20px;
padding: 10px;
color: #777;
font-size: 0.9em;
}
“`

7.2 テンプレートからの参照方法

テンプレートファイル (.html) から静的ファイルを参照するには、url_for() 関数を static エンドポイント名と filename 引数を使って利用します。

url_for('static', filename='path/to/file') という形式で記述します。filename の値は、static ディレクトリからの相対パスです。

templates/base.html を修正して、作成したCSSファイルを読み込むようにします。

“`html






{% block title %}My Flask App{% endblock %}
{# CSSファイルを読み込む #}

My Flask App


{% block content %}

コンテンツがありません。

{% endblock %}

© 2023 My Flask App


“`

サーバーを再起動し、任意のページにアクセスしてみてください。作成したCSSが適用され、ページの見た目が変わっているはずです。

ブラウザの開発者ツールを使ってソースコードを表示すると、{{ url_for('static', filename='css/style.css') }} の部分が <link rel="stylesheet" href="/static/css/style.css"> のように実際のURLに置き換えられていることが確認できます。

画像ファイルなど、他の静的ファイルも同様に url_for('static', filename='...') を使って参照できます。

  • <img src="{{ url_for('static', filename='images/logo.png') }}" alt="Logo">
  • <script src="{{ url_for('static', filename='js/script.js') }}"></script>

このように、static ディレクトリと url_for('static', ...) を使うことで、静的ファイルを効率的に管理し、テンプレートから正しく参照できます。

8. リクエストとレスポンス

ウェブアプリケーションは、クライアント(ブラウザなど)からの「リクエスト」を受け取り、それに対して「レスポンス」を返すことで機能します。Flaskは、これらのリクエストとレスポンスを扱うための便利な機能を提供します。

8.1 request オブジェクトの利用

クライアントから送信されたリクエストに関する情報(フォームデータ、クエリパラメータ、ヘッダー、ファイルなど)は、flask モジュールからインポートできる request オブジェクトを通じてアクセスできます。この request オブジェクトは、各リクエストごとに自動的に利用可能になります。

  • フォームデータの取得: HTTPのPOSTメソッドで送信されたフォームデータは、request.form 辞書ライクなオブジェクトで取得できます。
    python
    # 前述の login 関数内で使用した例
    username = request.form.get('username') # キーが存在しない場合は None を返す
    password = request.form['password'] # キーが存在しない場合はエラー (KeyError)

    get() メソッドを使う方が、キーが存在しない場合でもエラーにならないため安全です。
  • クエリパラメータの取得: HTTPのGETメソッドでURLに付け加えられたクエリパラメータ(例: /search?q=flask&page=1q=flask&page=1 部分)は、request.args 辞書ライクなオブジェクトで取得できます。
    python
    search_query = request.args.get('q')
    page_number = request.args.get('page', type=int, default=1) # typeで型変換、defaultでデフォルト値
  • ファイルアップロード: multipart/form-data 形式で送信されたファイルは、request.files 辞書ライクなオブジェクトで取得できます。
    python
    uploaded_file = request.files.get('file') # name="file" の input type="file" 要素
    if uploaded_file:
    # ファイル名を取得
    filename = uploaded_file.filename
    # ファイルを保存
    uploaded_file.save(f'/path/to/save/{filename}')
  • HTTPヘッダーの取得: リクエストヘッダーは request.headers オブジェクトで取得できます。
    python
    user_agent = request.headers.get('User-Agent')
  • リクエストメソッドの取得: リクエストのHTTPメソッドは request.method で取得できます ('GET', 'POST' など)。
  • リクエストURLの取得: request.url で完全なURL、request.path でパス部分を取得できます。

例として、検索クエリを受け取って表示するページを作成してみましょう。

“`python

app.py

from flask import Flask, request, render_template

app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’ # url_for のために必要

@app.route(‘/’)
def index():
return render_template(‘index.html’, …) # テンプレートは適宜用意

@app.route(‘/search’)
def search():
# クエリパラメータ ‘q’ の値を取得
query = request.args.get(‘q’)
if query:
# 検索処理を行う (ここでは仮に受け取ったクエリを表示するだけ)
search_results = [f”検索結果1 for ‘{query}'”, f”検索結果2 for ‘{query}'”]
return render_template(‘search_results.html’, query=query, results=search_results)
else:
# クエリが指定されていない場合は検索フォームを表示
return render_template(‘search_form.html’) # 検索フォーム用のテンプレート
“`

templates/search_form.html を作成します。

“`html

{% extends ‘base.html’ %}

{% block title %}検索フォーム – {{ super() }}{% endblock %}

{% block content %}

検索フォーム

{# GETメソッドで /search に送信 #}


{% endblock %}
“`

templates/search_results.html を作成します。

“`html

{% extends ‘base.html’ %}

{% block title %}検索結果 – {{ super() }}{% endblock %}

{% block content %}

「{{ query }}」の検索結果

{% if results %}

    {% for result in results %}

  • {{ result }}
  • {% endfor %}

{% else %}

検索結果が見つかりませんでした。

{% endif %}
{% endblock %}
“`

サーバーを起動し、http://127.0.0.1:5000/search にアクセスするとフォームが表示されます。何か入力して検索すると、入力したキーワードがクエリパラメータとしてURLに付加され (/search?q=...)、結果ページが表示されます。

8.2 レスポンスの生成

Flaskのビュー関数(@app.route で関連付けられた関数)は、最終的にクライアントに返される「レスポンス」を生成する必要があります。最もシンプルなレスポンスは文字列やJinja2テンプレートのレンダリング結果ですが、より複雑なレスポンスを返すこともできます。

  • 文字列: シンプルなテキストやHTML。Flaskが自動的にContent-Typeを text/html に設定します。
    python
    return "<h1>Hello!</h1>"
  • タプル: (response_body, status_code, headers) の形式で、レスポンスボディ、HTTPステータスコード、レスポンスヘッダーを細かく制御できます。
    “`python
    # 404 Not Found を返す例
    return “ページが見つかりません”, 404

    カスタムヘッダーを付ける例

    headers = {‘X-Custom-Header’: ‘Flask is cool’}
    return “カスタムヘッダー付きレスポンス”, 200, headers
    * **`jsonify` によるJSONレスポンス**: RESTful APIなどでよく使用されるJSON形式のレスポンスを簡単に生成できます。辞書やリストを渡すと、Flaskが自動的にJSON文字列に変換し、Content-Typeを `application/json` に設定します。python
    from flask import jsonify

    @app.route(‘/api/data’)
    def api_data():
    data = {
    ‘name’: ‘Alice’,
    ‘age’: 30,
    ‘is_active’: True
    }
    return jsonify(data)
    このエンドポイントにアクセスすると、ブラウザやAPIクライアントにはJSONデータが返されます。
    * **リダイレクト**: ユーザーを別のURLに転送します。
    python
    from flask import redirect, url_for

    @app.route(‘/old-url’)
    def old_url():
    # /new-url にリダイレクト(デフォルトは302 Found)
    return redirect(url_for(‘new_url’))

    @app.route(‘/new-url’)
    def new_url():
    return “これは新しいページです!”
    `url_for` を使うことで、エンドポイント名に基づいてリダイレクト先を指定できるため、URLが変更されてもコードの修正が少なくて済みます。
    * **アボート**: リクエスト処理を中断し、エラーコードとともにレスポンスを返します。
    python
    from flask import abort

    @app.route(‘/item/‘)
    def get_item(item_id):
    # 例: item_id が存在しない場合に 404 を返す
    item = find_item_by_id(item_id) # 実際にはデータベースなどから取得
    if item is None:
    abort(404) # 404 Not Found エラーを発生させる
    return f”アイテム: {item[‘name’]}”

    エラーハンドリングについては後述

    ``abort(404)` とすると、Flaskは内部的に404エラーを発生させ、対応するエラーハンドラー(後述)があればそれが実行されます。

request オブジェクトでクライアントからの情報を取得し、様々な形式で「レスポンス」を返すことで、インタラクティブなウェブアプリケーションを構築できます。

9. フォーム処理とバリデーション

ウェブアプリケーションの多くの機能は、ユーザーからの入力を受け付けるフォームと密接に関連しています。ログインフォーム、登録フォーム、お問い合わせフォーム、検索フォームなど、様々な場面でフォームが利用されます。

Flaskでは、request.form を使ってフォームデータにアクセスできることをすでに学びましたが、実際にはより複雑なフォーム処理やデータの検証(バリデーション)が必要になります。

9.1 HTMLフォームの作成

基本的なHTMLフォームは以下のように記述します。

“`html




“`

  • method="post": フォームデータをHTTPのPOSTメソッドで送信することを指定します。GETメソッドの場合は method="get" とします。
  • action="/process-form": フォームデータが送信される先のURLを指定します。このURLに対応するFlaskのルーティング関数を作成する必要があります。url_for() を使うのがおすすめです (action="{{ url_for('process_form') }}")。
  • name="...": 各入力要素(<input>, <textarea>, <select> など)には name 属性を付ける必要があります。この name 属性の値が、サーバー側でフォームデータを取得する際のキーとなります(例: request.form['name'])。

9.2 POSTメソッドでのデータ送信

HTMLフォームが method="post" で送信されると、ブラウザは入力されたデータをHTTPリクエストのボディに含めて action 属性で指定されたURLに送信します。Flaskでは、このデータを request.form で受け取ります。

例として、簡単な連絡フォームを作成し、送信された内容を表示する処理を実装します。

“`python

app.py

from flask import Flask, request, render_template, redirect, url_for

app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’ # url_for のために必要

連絡フォーム表示用ルーティング (GET)

@app.route(‘/contact’, methods=[‘GET’])
def contact_form():
return render_template(‘contact_form.html’)

フォームデータ受け取り・処理用ルーティング (POST)

@app.route(‘/contact’, methods=[‘POST’]) # 同じパスでもメソッドが違うので別の関数を関連付けられる
def process_contact_form():
# request.form からフォームデータを取得
name = request.form.get(‘name’)
email = request.form.get(‘email’)
message = request.form.get(‘message’)

# ここで取得したデータを使って何らかの処理を行う(例: メール送信、データベース保存)
# 今回は簡易的に取得したデータを表示するテンプレートにリダイレクト

# 処理後、別のページにリダイレクトするのが一般的(POST/Redirect/GET パターン)
# これにより、ブラウザの「戻る」ボタンやページのリロードによる二重送信を防ぐ
# データはクエリパラメータとして渡すか、セッションに保存するなどする
return redirect(url_for('contact_success', name=name, email=email, message=message))

フォーム送信成功ページ (GET)

@app.route(‘/contact/success’)
def contact_success():
# リダイレクトされた際のクエリパラメータからデータを取得
name = request.args.get(‘name’)
email = request.args.get(‘email’)
message = request.args.get(‘message’)
return render_template(‘contact_success.html’, name=name, email=email, message=message)

… (他のルーティングは省略) …

if name == ‘main‘:
app.run(debug=True)
“`

templates/contact_form.html を作成します。

“`html

{% extends ‘base.html’ %}

{% block title %}お問い合わせ – {{ super() }}{% endblock %}

{% block content %}

お問い合わせフォーム

{# POSTメソッドで送信 #}


{# requiredで必須入力 #}




{% endblock %}
“`

templates/contact_success.html を作成します。

“`html

{% extends ‘base.html’ %}

{% block title %}送信完了 – {{ super() }}{% endblock %}

{% block content %}

お問い合わせありがとうございます!

以下の内容で送信されました:

  • お名前: {{ name }}
  • メールアドレス: {{ email }}
  • お問い合わせ内容: {{ message }}

ホームに戻る

{% endblock %}
“`

サーバーを起動し、http://127.0.0.1:5000/contact にアクセスしてフォームを試してみてください。

入力後送信すると、データがサーバーに送信され、process_contact_form 関数で受け取られ、最後に contact_success ページにリダイレクトされて、入力内容が表示されます。

9.3 簡単なデータ検証(バリデーション)

フォームから受け取ったデータは、そのまま信用することはできません。必須項目の入力漏れ、不正な形式のデータ(メールアドレスの形式が違う、数値であるべきなのに文字列が入力されたなど)、セキュリティ上の問題(悪意のあるスクリプトの挿入など)がないか、サーバー側で検証(バリデーション)を行う必要があります。

request.form で受け取ったデータに対して、Pythonのコードで簡単なバリデーションを行うことができます。

“`python

app.py (process_contact_form 関数を修正)

from flask import Flask, request, render_template, redirect, url_for

app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’ # url_for のために必要

@app.route(‘/contact’, methods=[‘GET’, ‘POST’]) # GETとPOSTを同じ関数で処理
def contact():
if request.method == ‘POST’:
name = request.form.get(‘name’)
email = request.form.get(‘email’)
message = request.form.get(‘message’)

    errors = {} # エラーメッセージを格納する辞書

    # バリデーションルールを記述
    if not name:
        errors['name'] = 'お名前は必須です。'
    if not email:
        errors['email'] = 'メールアドレスは必須です。'
    # 簡単なメール形式チェック (より厳密なチェックは正規表現などを使用)
    elif '@' not in email or '.' not in email:
        errors['email'] = '有効なメールアドレスを入力してください。'
    if not message:
        errors['message'] = 'お問い合わせ内容は必須です。'
    elif len(message) > 500:
         errors['message'] = 'お問い合わせ内容は500文字以内にしてください。'

    # エラーがあるかチェック
    if errors:
        # エラーがある場合は、フォームページを再表示し、エラーメッセージと入力値を渡す
        return render_template('contact_form.html', errors=errors, values=request.form)
    else:
        # エラーがない場合は処理を続行(例: メール送信、データベース保存)
        print(f"お問い合わせ受信: 名前={name}, メール={email}, 内容={message}") # 開発用出力
        # 成功ページにリダイレクト
        return redirect(url_for('contact_success')) # successページではデータ表示しない場合は引数不要

# GET リクエストの場合はフォームを表示
return render_template('contact_form.html')

成功ページはデータを表示しないシンプルなものに変更

@app.route(‘/contact/success’)
def contact_success():
return render_template(‘contact_success.html’)

… (他のルーティングは省略) …

if name == ‘main‘:
app.run(debug=True)
“`

templates/contact_form.html を修正して、エラーメッセージと入力値を表示できるようにします。

“`html

{% extends ‘base.html’ %}

{% block title %}お問い合わせ – {{ super() }}{% endblock %}

{% block content %}

お問い合わせフォーム

{# errors 辞書が存在する場合にエラーのリストを表示 #}
{% if errors %}

エラー:

    {% for field, error in errors.items() %}

  • {{ error }}
  • {% endfor %}

{% endif %}

<form method="post" action="{{ url_for('contact') }}"> {# actionをcontact() に変更 #}
    <div>
        <label for="name">お名前:</label><br>
        {# values.name で入力値を保持 #}
        <input type="text" id="name" name="name" value="{{ values.name if values }}" required>
        {# 特定のフィールドのエラーメッセージも表示可能 #}
        {% if errors.name %}<span style="color: red;">{{ errors.name }}</span>{% endif %}
    </div>
    <br>
    <div>
        <label for="email">メールアドレス:</label><br>
        <input type="email" id="email" name="email" value="{{ values.email if values }}" required>
         {% if errors.email %}<span style="color: red;">{{ errors.email }}</span>{% endif %}
    </div>
    <br>
    <div>
        <label for="message">お問い合わせ内容:</label><br>
        <textarea id="message" name="message" rows="5" required>{{ values.message if values }}</textarea>
        {% if errors.message %}<span style="color: red;">{{ errors.message }}</span>{% endif %}
    </div>
    <br>
    <input type="submit" value="送信">
</form>

{% endblock %}
`templates/contact_success.html` もシンプルに変更。html

{% extends ‘base.html’ %}

{% block title %}送信完了 – {{ super() }}{% endblock %}

{% block content %}

お問い合わせありがとうございます!

お問い合わせ内容が正常に送信されました。

ホームに戻る

{% endblock %}
“`

これで、フォーム送信時にサーバー側でバリデーションが行われ、エラーがあればエラーメッセージとともにフォームが再表示され、入力値も保持されるようになります。

9.4 WTFormsの紹介

上記の例のように、簡単なバリデーションであれば手書きで実現できますが、フォームの項目が増えたり、複雑なバリデーションが必要になったりすると、コードが煩雑になりがちです。

より高度なフォーム処理とバリデーションを行うために、Flaskでは Flask-WTF という拡張機能がよく利用されます。Flask-WTFは、Pythonでフォームの構造を定義し、バリデーションルールを指定できるWTFormsライブラリをFlaskと連携させるための拡張機能です。

Flask-WTFを使うと、以下のようなメリットがあります。

  • フォーム定義の構造化: Pythonのクラスとしてフォームのフィールドやバリデーションルールを定義できます。
  • 豊富なバリデーター: 必須チェック、メール形式、数値、長さなど、様々な組み込みバリデーターが利用できます。カスタムバリデーターも作成可能です。
  • CSRF保護: クロスサイトリクエストフォージェリ(CSRF)攻撃からフォームを保護するための機能が簡単に利用できます。
  • テンプレートヘルパー: テンプレート内でフォームフィールドを簡単にレンダリングするためのヘルパー関数が提供されます。

Flask-WTFをインストールするには、仮想環境をアクティベートした状態で以下のコマンドを実行します。

bash
(venv) user@host:~/myflaskapp$ pip install Flask-WTF

Flask-WTFを使うには、まずFlaskアプリケーションに SECRET_KEY を設定する必要があります(CSRF保護などで使用されるため)。これは既に行っています。

次に、forms.py のようなファイルを作成し、WTFormsを使ってフォームクラスを定義します。

“`python

forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Email, Length

class ContactForm(FlaskForm):
name = StringField(‘お名前’, validators=[DataRequired()]) # 必須入力
email = StringField(‘メールアドレス’, validators=[DataRequired(), Email()]) # 必須かつメール形式
message = TextAreaField(‘お問い合わせ内容’, validators=[DataRequired(), Length(min=10, max=500)]) # 必須かつ長さ制限
submit = SubmitField(‘送信’)
“`

そして、app.py でこのフォームクラスを使用します。

“`python

app.py (一部修正)

from flask import Flask, render_template, redirect, url_for
from forms import ContactForm # 作成したフォームクラスをインポート

app = Flask(name)
app.config[‘SECRET_KEY’] = ‘your_secret_key_here’ # 必ず設定すること!

@app.route(‘/contact’, methods=[‘GET’, ‘POST’])
def contact():
form = ContactForm() # フォームインスタンスを作成

if form.validate_on_submit(): # POSTメソッドで送信され、かつバリデーションに成功した場合
    # バリデーション済みデータは form.data 辞書や form.field_name.data で取得できる
    name = form.name.data
    email = form.email.data
    message = form.message.data

    # ここで取得したデータを使って処理を行う(例: メール送信、データベース保存)
    print(f"お問い合わせ受信 (WTF): 名前={name}, メール={email}, 内容={message}") # 開発用出力

    # 成功ページにリダイレクト
    return redirect(url_for('contact_success'))

# GET リクエストの場合、またはPOSTでバリデーションに失敗した場合はフォームを表示
# バリデーションエラーは form.errors に格納されている
return render_template('contact_form_wtf.html', form=form) # 新しいテンプレートを使用

@app.route(‘/contact/success’)
def contact_success():
return render_template(‘contact_success.html’) # 成功ページは同じものを使用

… (他のルーティングは省略) …

if name == ‘main‘:
app.run(debug=True)
“`

templates/contact_form_wtf.html を作成します。Flask-WTFのテンプレートヘルパーを使います。

“`html

{% extends ‘base.html’ %}
{% from ‘macros.html’ import render_field %} {# マクロをインポート(後述) #}

{% block title %}お問い合わせ (WTF) – {{ super() }}{% endblock %}

{% block content %}

お問い合わせフォーム (WTForms)

{# form.errors でバリデーションエラー全体を取得できる #}
{% if form.errors %}
    <div style="color: red;">
        <h3>エラー:</h3>
        <ul>
            {% for field, errors in form.errors.items() %}
                {% for error in errors %}
                    <li>{{ form[field].label.text }}: {{ error }}</li> {# フィールド名とエラーメッセージを表示 #}
                {% endfor %}
            {% endfor %}
        </ul>
    </div>
{% endif %}

<form method="post" action="{{ url_for('contact') }}">
    {{ form.csrf_token }} {# CSRFトークンを埋め込む(必須) #}

    {# 各フォームフィールドを個別にレンダリング #}
    <div>
        {{ form.name.label }}<br> {# ラベルをレンダリング #}
        {{ form.name() }} {# input タグをレンダリング #}
        {% if form.name.errors %}<ul style="color: red;">{% for error in form.name.errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %} {# 個別フィールドのエラー #}
    </div>
    <br>
    <div>
        {{ form.email.label }}<br>
        {{ form.email() }}
         {% if form.email.errors %}<ul style="color: red;">{% for error in form.email.errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %}
    </div>
    <br>
    <div>
        {{ form.message.label }}<br>
        {{ form.message() }}
         {% if form.message.errors %}<ul style="color: red;">{% for error in form.message.errors %}<li>{{ error }}</li>{% endfor %}</ul>{% endif %}
    </div>
    <br>
    <div>
        {{ form.submit() }}
    </div>
</form>

{% endblock %}
“`

上記のテンプレートは少し長いですが、{{ form.field_name.label }}{{ form.field_name() }} を使うことで、Pythonで定義したフォームフィールドを簡単にHTMLとして出力できます。また、form.csrf_token はCSRF保護のために必須です。

テンプレートをよりDRY (Don’t Repeat Yourself) にするために、フォームフィールドのレンダリングをマクロとして定義することがよくあります。templates ディレクトリに macros.html というファイルを作成します。

html
{# templates/macros.html #}
{% macro render_field(field) %}
<div>
{{ field.label }}<br>
{{ field(**kwargs) }} {# field() に渡された追加属性もレンダリング #}
{% if field.errors %}
<ul style="color: red;">
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endmacro %}

そして、contact_form_wtf.html を修正して、このマクロを使います。

“`html

{% extends ‘base.html’ %}
{% from ‘macros.html’ import render_field %} {# マクロをインポート #}

{% block title %}お問い合わせ (WTF) – {{ super() }}{% endblock %}

{% block content %}

お問い合わせフォーム (WTForms)

{% if form.errors %}
    <div style="color: red;">
        <h3>エラー:</h3>
        <ul>
            {% for field, errors in form.errors.items() %}
                {% for error in errors %}
                    <li>{{ form[field].label.text }}: {{ error }}</li>
                {% endfor %}
            {% endfor %}
        </ul>
    </div>
{% endif %}

<form method="post" action="{{ url_for('contact') }}">
    {{ form.csrf_token }}

    {{ render_field(form.name) }} {# マクロを使ってレンダリング #}
    <br>
    {{ render_field(form.email) }}
    <br>
    {{ render_field(form.message) }}
    <br>
    {{ form.submit() }} {# SubmitButtonはマクロを使わないことが多い #}
</form>

{% endblock %}
“`

これで、Flask-WTFを使ったフォーム処理とバリデーションの実装ができました。Flask-WTFは、より堅牢で管理しやすいフォーム処理を可能にするため、実際のアプリケーション開発では積極的に活用したい拡張機能です。

10. データベース連携

ほとんどのウェブアプリケーションでは、データを永続的に保存・取得するためにデータベースが必要です。Flask自体は特定のデータベースに依存していませんが、SQLiteのような組み込みデータベースを簡単に扱う方法や、SQLAlchemyのようなORM(Object-Relational Mapper)を利用するための拡張機能が提供されています。

ここでは、Python標準ライブラリに含まれるSQLite3を使って簡単なデータベース連携の例を示し、次にFlaskでよく使われるFlask-SQLAlchemyを紹介します。

10.1 SQLite3の利用例

SQLiteは、サーバーとして動作するのではなく、ファイルとしてデータベースを管理する軽量なデータベースです。設定が不要で手軽に使えるため、小規模なアプリケーションやプロトタイプ、開発用途に適しています。

SQLite3をFlaskアプリケーションで使用するには、Pythonの標準ライブラリ sqlite3 モジュールを利用します。

データベース接続を管理するために、アプリケーションコンテキストやリクエストコンテキストを利用するのが一般的です。ここでは、簡単な例として関数内で接続・操作する方法を示します。

“`python

app.py (一部追加)

import sqlite3
from flask import Flask, render_template, g # g はリクエストコンテキスト内のオブジェクト

app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’ # url_for のために必要
app.config[‘DATABASE’] = ‘mydatabase.db’ # データベースファイル名

データベース接続を取得するヘルパー関数

def get_db():
# リクエストコンテキスト変数 g に ‘db’ がない場合(初めて接続する場合)
if ‘db’ not in g:
# データベースに接続
g.db = sqlite3.connect(
app.config[‘DATABASE’],
detect_types=sqlite3.PARSE_DATES # 日付型を自動変換
)
# レスポンスが返された後に接続を閉じるように設定
g.db.row_factory = sqlite3.Row # 結果を辞書のようにアクセスできるように設定
return g.db

アプリケーションコンテキストが終了する際にデータベース接続を閉じる処理

@app.teardown_appcontext
def close_db(e=None):
db = g.pop(‘db’, None) # g から ‘db’ を取得し、存在しなければ None

if db is not None:
    db.close() # 接続を閉じる

データベースを初期化する関数 (開発用)

def init_db():
db = get_db()
with app.open_resource(‘schema.sql’, mode=’r’) as f:
db.executescript(f.read())
db.commit()
print(‘Initialized the database.’)

CLIコマンドとしてデータベース初期化を追加 (Optional: Click が必要)

import click
@app.cli.command(‘init-db’)
def init_db_command():
“””Clear existing data and create new tables.”””
init_db()
click.echo(‘Initialized the database.’) # ターミナルに出力

ここからルーティング関数

例: データベースからユーザー一覧を取得して表示

@app.route(‘/users’)
def list_users():
db = get_db()
# データベースからデータを取得
users = db.execute(‘SELECT id, username FROM users’).fetchall()
return render_template(‘users.html’, users=users)

例: 新しいユーザーを追加する (POSTリクエストで受け取る想定)

簡略化のため、今回はGETリクエストで固定データを挿入する例

@app.route(‘/add_user’)
def add_user():
db = get_db()
username = “test_user_” + str(sqlite3.connect(app.config[‘DATABASE’]).execute(‘SELECT COUNT(*) FROM users’).fetchone()[0] + 1) # 仮のユーザー名
db.execute(‘INSERT INTO users (username) VALUES (?)’, (username,))
db.commit() # 変更を保存
# ユーザー一覧ページにリダイレクト
return redirect(url_for(‘list_users’))

… (他のルーティングは省略) …

if name == ‘main‘:
# アプリケーションインスタンスを作成した後、CLIコマンドでデータベース初期化できるように
# flask run で実行する場合、この if ブロックは実行されないが、CLIコマンドは登録される
# python app.py で実行する場合は init_db() など手動で呼び出す必要がある
app.run(debug=True)

“`

上記コードでは、get_db() 関数でデータベース接続を取得し、@app.teardown_appcontext デコレーターを使ってリクエスト処理後に接続を閉じるように設定しています。また、sqlite3.Row を使うと、取得した結果を辞書のように扱えるため便利です。

データベースのスキーマ(テーブル構造)を定義するために、schema.sql ファイルを作成します。

“`sql
— schema.sql
DROP TABLE IF EXISTS users;

CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL
);
“`

データベースを初期化するには、仮想環境をアクティベートしたターミナルで以下のコマンドを実行します。

bash
(venv) user@host:~/myflaskapp$ flask init-db
Initialized the database.

これにより mydatabase.db ファイルが作成され、users テーブルが作られます。

ユーザー一覧を表示するためのテンプレート templates/users.html を作成します。

“`html

{% extends ‘base.html’ %}

{% block title %}ユーザー一覧 – {{ super() }}{% endblock %}

{% block content %}

登録ユーザー一覧

    {% for user in users %}

  • ID: {{ user[‘id’] }}, ユーザー名: {{ user[‘username’] }}
  • {# userはRowオブジェクトなので辞書のようにアクセス #}
    {% else %} {# users リストが空の場合 #}

  • ユーザーは登録されていません。
  • {% endfor %}

テストユーザーを追加

{% endblock %}
“`

サーバーを起動し、http://127.0.0.1:5000/users にアクセスするとユーザー一覧が表示されます。最初は空ですが、「テストユーザーを追加」リンクをクリックするとユーザーが追加され、ページが更新されて追加されたユーザーが表示されます。

この例はSQLite3を直接扱っていますが、多くのウェブフレームワークと同様に、生のSQLを扱うのはアプリケーションが複雑になると管理が難しくなります。

10.2 Flask-SQLAlchemyの紹介

より本格的なデータベース連携には、ORM(Object-Relational Mapper)の利用が推奨されます。ORMは、データベースのテーブルをPythonのクラス(モデル)として扱い、SQLクエリをPythonのコードで記述できるようにするツールです。これにより、データベースの種類に依存しないコードを書くことができ、生産性や保守性が向上します。

Pythonで最も広く使われているORMの一つがSQLAlchemyです。Flask-SQLAlchemyは、このSQLAlchemyをFlaskで使いやすくするための拡張機能です。

Flask-SQLAlchemyをインストールするには、仮想環境をアクティベートした状態で以下のコマンドを実行します。

bash
(venv) user@host:~/myflaskapp$ pip install Flask-SQLAlchemy

Flask-SQLAlchemyを使うには、アプリケーションにデータベースのURI(接続情報)を設定し、SQLAlchemy クラスのインスタンスを作成します。

“`python

app.py (Flask-SQLAlchemyを使う例)

from flask import Flask, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy # SQLAlchemyをインポート

app = Flask(name)

データベースURIを設定

SQLiteの場合: ‘sqlite:///path/to/database.db’

アプリケーションインスタンスのあるディレクトリに database.db を作成する場合:

app.config[‘SQLALCHEMY_DATABASE_URI’] = ‘sqlite:///site.db’
app.config[‘SQLALCHEMY_TRACK_MODIFICATIONS’] = False # 変更追跡を無効化 (推奨)
app.config[‘SECRET_KEY’] = ‘…’ # url_for などで使用

SQLAlchemy インスタンスを作成し、Flask アプリケーションと関連付ける

db = SQLAlchemy(app)

データベースモデルを定義 (データベースのテーブルに対応するPythonクラス)

class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)

# モデルを文字列で表現するメソッド (デバッグなどで便利)
def __repr__(self):
    return '<User %r>' % self.username

データベーステーブルを作成するコマンド (開発用)

@app.cli.command(‘create-db’)
def create_db_command():
“””Create database tables.”””
db.create_all() # モデル定義に基づいてテーブルを作成
print(‘Initialized the database tables.’)

ここからルーティング関数

例: ユーザーを追加するページ (簡易版、POSTフォームから受け取るのが一般的)

@app.route(‘/add_new_user//‘)
def add_new_user(username, email):
# 新しいユーザーインスタンスを作成
new_user = User(username=username, email=email)
# セッションに追加
db.session.add(new_user)
# データベースに変更を保存
db.session.commit()
return f”ユーザー {username} が追加されました!”

例: 全ユーザー一覧を表示

@app.route(‘/users_sqlalchemy’)
def list_users_sqlalchemy():
# データベースから全ユーザーを取得
users = User.query.all()
return render_template(‘users_sqlalchemy.html’, users=users)

… (他のルーティングは省略) …

if name == ‘main‘:
# Flask-SQLAlchemyを使用する場合、db.create_all() を実行してテーブルを作成する必要がある
# 通常はCLIコマンド ‘create-db’ を使うか、アプリケーション起動時に初期化関数を呼ぶ
# db.create_all() # python app.py で実行する場合の例 (非推奨、CLIコマンドが推奨)
app.run(debug=True)
“`

データベーステーブルを作成するには、仮想環境をアクティベートしたターミナルで以下のコマンドを実行します。

bash
(venv) user@host:~/myflaskapp$ flask create-db
Initialized the database tables.

これにより、アプリケーションインスタンスのあるディレクトリに site.db ファイルが作成され、users テーブル(Userモデルに対応)が作成されます。

ユーザー一覧を表示するためのテンプレート templates/users_sqlalchemy.html を作成します。

“`html

{% extends ‘base.html’ %}

{% block title %}ユーザー一覧 (SQLAlchemy) – {{ super() }}{% endblock %}

{% block content %}

登録ユーザー一覧 (SQLAlchemy)

    {% for user in users %}

  • ID: {{ user.id }}, ユーザー名: {{ user.username }}, メール: {{ user.email }}
  • {% else %}

  • ユーザーは登録されていません。
  • {% endfor %}

{# 簡単な追加リンク。実際はフォームを使用 #}

テストユーザー追加:
test_user_1 |
test_user_2

{% endblock %}
“`

サーバーを起動し、http://127.0.0.1:5000/users_sqlalchemy にアクセスするとユーザー一覧が表示されます。最初は空ですが、テストユーザー追加リンクをクリックするとユーザーが追加され、一覧に表示されます。

Flask-SQLAlchemyを使うことで、データベース操作をPythonのオブジェクトとして扱うことができ、より直感的でメンテナンス性の高いコードを書くことができます。実際のアプリケーション開発では、SQLAlchemyやFlask-SQLAlchemyのようなORMを利用するのが一般的です。

11. セッションとクッキー

HTTPプロトコルは、基本的にはステートレスです。つまり、サーバーは個々のリクエストを独立したものとして扱い、以前のリクエストの状態を記憶しません。しかし、ウェブアプリケーションでは、ユーザーがログインしている状態を維持したり、ショッピングカートの内容を記録したり、ユーザー設定を保存したりするなど、クライアントの状態を保持する必要がよくあります。

これを実現するために、「セッション」と「クッキー」が利用されます。

11.1 セッション

セッションは、サーバー側にユーザー固有の情報を保存する仕組みです。クライアント(ブラウザ)には「セッションID」だけがクッキーとして保存され、サーバーはこのIDを使って対応するセッションデータを取り出します。

セッションの主な利点は以下の通りです。

  • セキュリティ: ユーザーの機密情報(ログイン状態など)がサーバー側に保存されるため、クライアント側に保存するよりも安全です。
  • データ容量: クッキーのように容量制限を気にせず、サーバーのメモリやデータベースが許す限り多くのデータを保存できます。

Flaskでセッションを使用するには、アプリケーションに SECRET_KEY を設定する必要があります。このキーはセッションデータ(クライアントにクッキーとして保存される、暗号化されたデータ)を署名するために使用されます。予測困難な安全な文字列を使用することが非常に重要です。

“`python

app.py (セッションを使う例)

from flask import Flask, render_template, request, session, redirect, url_for

app = Flask(name)

セキュアな秘密鍵を設定(必ず実際の開発ではランダムな文字列に変更すること!)

app.config[‘SECRET_KEY’] = ‘very-secret-random-key-please-change’

例: ログイン処理 (簡易版)

@app.route(‘/login_session’, methods=[‘GET’, ‘POST’])
def login_session():
if request.method == ‘POST’:
username = request.form.get(‘username’)
password = request.form.get(‘password’)

    # ここでユーザー名とパスワードを検証する処理 (今回は省略)
    # 認証成功したと仮定
    if username == 'test' and password == 'password':
        # セッションにユーザー名を保存(ログイン状態にする)
        session['username'] = username
        # ログイン成功後のページにリダイレクト
        return redirect(url_for('profile'))
    else:
        # 認証失敗
        return "ログイン失敗"

# GET リクエストの場合、ログインフォームを表示
return '''
    <form method="post">
        <p><input type="text" name="username" placeholder="ユーザー名"></p>
        <p><input type="password" name="password" placeholder="パスワード"></p>
        <p><input type="submit" value="ログイン"></p>
    </form>
'''

例: ログイン状態を確認し、プロフィールページを表示

@app.route(‘/profile’)
def profile():
# セッションからユーザー名を取得
username = session.get(‘username’) # session.get() はキーが存在しない場合に None を返す
if username:
# ログインしている場合
return f’こんにちは、{username}さん!
ログアウト
else:
# ログインしていない場合、ログインページにリダイレクト
return redirect(url_for(‘login_session’))

例: ログアウト処理

@app.route(‘/logout_session’)
def logout_session():
# セッションからユーザー名を削除
session.pop(‘username’, None) # pop() はキーが存在しない場合でもエラーにならないように None を指定
# セッション全体をクリアする場合: session.clear()
return ‘ログアウトしました。
ホームに戻る

… (他のルーティングは省略) …

if name == ‘main‘:
app.run(debug=True)
“`

session オブジェクトは辞書のように扱えます。キーと値のペアを代入することでセッションにデータを保存し、キーを指定して値を取得できます。session.get('key') はキーが存在しない場合に None を返すため、存在チェックをする際に便利です。session.pop('key', None) はキーを削除します。

この例では、ユーザーがログインするとセッションにユーザー名が保存され、別のページ (/profile) にアクセスしてもログイン状態が維持されます。ログアウトするとセッションからユーザー名が削除され、ログイン状態が解除されます。

11.2 クッキー

クッキーは、クライアント(ブラウザ)側にユーザー固有の情報を保存する仕組みです。サーバーはレスポンスヘッダーに Set-Cookie を含めてクッキーをクライアントに送信し、クライアントは以降のリクエストヘッダーにそのクッキーを含めてサーバーに送信します。

クッキーの主な利点は以下の通りです。

  • サーバー負荷軽減: データをクライアント側に保存するため、サーバーのメモリやストレージを消費しません。

欠点としては、以下の点が挙げられます。

  • セキュリティリスク: クライアント側にデータが保存されるため、悪意のあるユーザーによって改変されたり、盗まれたりするリスクがあります。重要な情報はクッキーに直接保存すべきではありません。
  • 容量制限: 一般的に、クッキー1つあたり数KB、ドメイン全体で数十KB程度の容量制限があります。
  • ユーザー設定: ユーザーがブラウザでクッキーを無効にしている場合があります。

Flaskでクッキーを設定・取得するには、request.cookies と、レスポンスオブジェクトが必要です。

“`python

app.py (クッキーを使う例)

from flask import Flask, make_response, request

app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’ # url_for などで使用

例: クッキーを設定する

@app.route(‘/set_cookie’)
def set_cookie():
# レスポンスオブジェクトを作成
response = make_response(“クッキーを設定しました!”)
# クッキーを設定 (キー, 値, 有効期限など)
response.set_cookie(‘username’, ‘Alice’, max_age=606024*30) # 30日間有効
response.set_cookie(‘last_visit’, ‘now’) # 有効期限を指定しないとブラウザを閉じると削除される
return response

例: クッキーを取得する

@app.route(‘/get_cookie’)
def get_cookie():
# request.cookies からクッキーの値を取得
username = request.cookies.get(‘username’)
last_visit = request.cookies.get(‘last_visit’)

if username:
    return f'保存されているユーザー名: {username}<br>最終訪問: {last_visit}'
else:
    return 'クッキーが見つかりませんでした。'

例: クッキーを削除する

@app.route(‘/delete_cookie’)
def delete_cookie():
response = make_response(“クッキーを削除しました!”)
# max_age に 0 を設定するか、expires に過去の日付を設定することで削除
response.set_cookie(‘username’, ”, max_age=0)
response.delete_cookie(‘last_visit’) # delete_cookie メソッドもある
return response

… (他のルーティングは省略) …

if name == ‘main‘:
app.run(debug=True)
“`

クッキーを設定するには、まず make_response() 関数を使ってレスポンスオブジェクトを生成し、そのオブジェクトの set_cookie() メソッドを使用します。クッキーの値を取得するには、request.cookies 辞書ライクなオブジェクトを使います。クッキーを削除するには、max_age=0 を指定して同じ名前のクッキーを設定するか、delete_cookie() メソッドを使用します。

通常、ユーザーのログイン状態など、サーバー側で管理すべき重要な情報はセッションに保存し、ユーザーの好み設定やトラッキング情報など、比較的安全な情報はクッキーに保存するという使い分けがされます。Flaskのセッションは、内部的にクッキーを使ってセッションIDをやり取りしているため、クッキーが無効なブラウザではセッションも機能しない点に注意が必要です。

12. エラーハンドリング

ウェブアプリケーションでは、様々な状況でエラーが発生する可能性があります。ユーザーが存在しない(404 Not Found)、サーバー内部でエラーが発生した(500 Internal Server Error)、権限がない(403 Forbidden)など、様々なHTTPエラーや例外が発生します。これらのエラーが発生した際に、ユーザーに分かりやすいエラーページを表示することは、ユーザー体験の向上やデバッグ情報の漏洩防止のために重要です。

Flaskでは、特定のエラーコードや例外が発生した際に実行される「エラーハンドラー」を定義できます。

12.1 HTTPエラーのハンドリング

特定のHTTPステータスコード(例: 404, 500)に対するエラーページをカスタマイズするには、@app.errorhandler() デコレーターを使用します。

“`python

app.py (エラーハンドリングの例)

from flask import Flask, render_template, abort

app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’ # url_for などで使用

… (他のルーティングは省略) …

404 Not Found エラーのハンドリング

@app.errorhandler(404)
def page_not_found(error):
# カスタムエラーページ用のテンプレートをレンダリング
# レスポンスはタプル形式で返す (ボディ, ステータスコード)
return render_template(‘404.html’), 404

500 Internal Server Error のハンドリング

@app.errorhandler(500)
def internal_server_error(error):
# カスタムエラーページ用のテンプレートをレンダリング
return render_template(‘500.html’), 500

例: 意図的に404エラーを発生させるルーティング

@app.route(‘/nonexistent’)
def nonexistent_page():
# abort(404) を呼び出すと、上記の @app.errorhandler(404) が捕捉する
abort(404)
# この行以降のコードは実行されない
return “これは表示されません”

例: 意図的に例外を発生させるルーティング

@app.route(‘/cause_error’)
def cause_error():
result = 1 / 0 # ゼロ除算エラー (ZeroDivisionError)
return “エラーが発生しました”

特定の例外を捕捉することも可能

@app.errorhandler(ZeroDivisionError)

def handle_zero_division(error):

return “0で割ろうとしました!”, 500

if name == ‘main‘:
# デバッグモードが True の場合、500エラーハンドラーは捕捉されないことがある(デバッガーが優先されるため)
app.run(debug=True)
“`

カスタムエラーページ用のテンプレートを作成します。templates/404.htmltemplates/500.html を作成します。

“`html

{% extends ‘base.html’ %}

{% block title %}ページが見つかりません – {{ super() }}{% endblock %}

{% block content %}

404 – ページが見つかりません

お探しのページは見つかりませんでした。URLを確認してください。

ホームに戻る

{% endblock %}
“`

“`html

{% extends ‘base.html’ %}

{% block title %}サーバーエラー – {{ super() }}{% endblock %}

{% block content %}

500 – 内部サーバーエラー

申し訳ありませんが、サーバーで予期せぬエラーが発生しました。

しばらくしてから再度お試しいただくか、サイト管理者にお問い合わせください。

{% endblock %}
“`

サーバーを起動し、存在しないURL(例: http://127.0.0.1:5000/nonexistent-page)にアクセスしてみてください。作成した404エラーページが表示されるはずです。
また、http://127.0.0.1:5000/cause_error にアクセスすると、通常はデバッグモードの詳細なエラー画面が表示されます(debug=True の場合)。デバッグモードを False にして実行するか、本番環境で発生した場合は、作成した500エラーページが表示されます。

デバッグモード (debug=True) では、エラー発生時に詳細なスタックトレースを含むウェブベースのデバッガーが表示されるため、開発中はデバッグモードを有効にしておくのが便利です。しかし、本番環境ではデバッグモードを無効にし、カスタムエラーページを表示するように設定することが非常に重要です。デバッグモードは機密情報を漏洩させる可能性があります。

13. アプリケーション構成のスケールアップ

これまでに見てきた例では、すべてのコードを app.py という一つのファイルに記述してきました。小規模なアプリケーションであればこれでも十分ですが、機能が増えてコード量が多くなってくると、一つのファイルで管理するのは難しくなります。コードの見通しが悪くなり、保守や機能追加が困難になります。

アプリケーションの規模が大きくなってきたら、コードを複数のファイルやディレクトリに分割し、整理することを検討する必要があります。

13.1 Blueprintの紹介

Flaskには、アプリケーションをより小さな再利用可能な単位に分割するための Blueprint (ブループリント) という機能があります。Blueprintは、ルーティング、テンプレート、静的ファイル、エラーハンドラーなどの定義をまとめておくことができます。

Blueprintを使うことで、以下のメリットがあります。

  • モジュール化: アプリケーションを機能ごと(例: ユーザー管理、ブログ、管理画面)に分割し、それぞれのコードを独立して管理できます。
  • 再利用性: 作成したBlueprintは、他のFlaskアプリケーションに組み込んで再利用できます。
  • URLプレフィックス: Blueprint内のすべてのルートに共通のURLプレフィックス(例: /admin, /blog)を設定できます。
  • サブドメイン: Blueprintを特定のサブドメインに紐づけることも可能です。

Blueprintを使用するには、まずBlueprintインスタンスを作成します。

“`python

users_bp.py (ユーザー管理機能のBlueprint)

from flask import Blueprint, render_template, request, redirect, url_for

Blueprint インスタンスを作成

第一引数: Blueprintの名前 (一意である必要)

第二引数: Blueprintのルートディレクトリを決定するためのモジュール名 (name)

url_prefix: このBlueprint内のすべてのルートに適用されるURLプレフィックス

users_bp = Blueprint(‘users’, name, url_prefix=’/users’)

このBlueprint内のルーティングを定義

@users_bp.route(‘/’)
def index():
# ユーザー一覧を表示するテンプレート
users = [“Alice”, “Bob”, “Charlie”] # 仮のユーザーデータ
return render_template(‘users_bp/index.html’, users=users) # テンプレートは Blueprint 名をサブディレクトリとして配置するのが慣習

@users_bp.route(‘/‘)
def show_user_profile(username):
return render_template(‘users_bp/profile.html’, username=username) # テンプレートは Blueprint 名をサブディレクトリとして配置するのが慣習

他のユーザー関連のルーティングもここに追加

@users_bp.route(‘/register’, methods=[‘GET’, ‘POST’])

def register():

pass

“`

templates ディレクトリ内に、Blueprint名と同じ名前のサブディレクトリ (users_bp) を作成し、その中にテンプレートファイル (index.html, profile.html など) を配置するのが一般的な慣習です。

“`html

{% extends ‘base.html’ %}

{% block title %}ユーザー一覧 (BP) – {{ super() }}{% endblock %}

{% block content %}

ユーザー一覧 (Blueprint)

    {% for user in users %}

  • {{ user }}
  • {# Blueprint名.関数名 で url_for を使う #}
    {% endfor %}

{% endblock %}
“`

“`html

{% extends ‘base.html’ %}

{% block title %}{{ username }} のプロフィール – {{ super() }}{% endblock %}

{% block content %}

{{ username }} のプロフィール

これは {{ username }} さんのプロフィールページです。

ユーザー一覧に戻る

{% endblock %}
“`

Blueprintを定義したら、メインのFlaskアプリケーションインスタンスに「登録」する必要があります。

“`python

app.py (Blueprintを登録する例)

from flask import Flask
from users_bp import users_bp # 作成したBlueprintをインポート

app = Flask(name)
app.config[‘SECRET_KEY’] = ‘…’

Blueprintをアプリケーションに登録

app.register_blueprint(users_bp)

オプションで url_prefix をここで指定することも可能(Blueprint定義時とどちらかで)

app.register_blueprint(users_bp, url_prefix=’/users’)

… (他の共通ルーティングやエラーハンドラーなどは app.py に置く) …

@app.route(‘/’)
def index():
return ‘これはメインのトップページです。
ユーザー一覧 (Blueprint)

if name == ‘main‘:
app.run(debug=True)
“`

サーバーを起動し、http://127.0.0.1:5000/users/http://127.0.0.1:5000/users/Alice にアクセスしてみてください。Blueprintで定義したルーティングが機能し、テンプレートがレンダリングされるはずです。

url_for() 関数を使う際は、Blueprint名と関数名をピリオド (.) で区切って指定します(例: url_for('users.index'))。

このようにBlueprintを使うことで、アプリケーションのコードを論理的なまとまりに分割し、管理しやすくなります。

13.2 パッケージ構成の例

規模が大きくなったFlaskアプリケーションは、通常、以下のようなパッケージ構成をとることが多いです。

myflaskapp/
├── venv/
├── app.py # アプリケーションインスタンスの作成、設定、Blueprintの登録など
├── config.py # アプリケーション設定 (SECRET_KEY, DB URIなど)
├── forms.py # WTFormsによるフォーム定義
├── models.py # SQLAlchemyなどによるデータベースモデル定義
├── routes/ # Blueprintごとにディレクトリを分割
│ ├── __init__.py # routes パッケージの初期化
│ ├── users.py # ユーザー関連のBlueprint定義 (users_bp)
│ └── blog.py # ブログ関連のBlueprint定義 (blog_bp)
├── templates/ # テンプレートファイル
│ ├── base.html
│ ├── macros.html
│ ├── users_bp/ # users Blueprint用テンプレート
│ │ ├── index.html
│ │ └── profile.html
│ └── blog_bp/ # blog Blueprint用テンプレート
│ └── index.html
└── static/ # 静的ファイル
└── css/
└── style.css

この構成では、app.py がアプリケーションのエントリーポイントとなり、設定を読み込み、Blueprintsを登録し、開発サーバーを起動します。各機能(ユーザー、ブログなど)は routes ディレクトリ内の独立したファイルでBlueprintとして定義され、それぞれのファイルでルーティングやテンプレート、静的ファイルなどが管理されます。設定は config.py に、フォーム定義は forms.py に、データベースモデルは models.py にといった具合に、役割ごとにファイルが分割されます。

このようなパッケージ構成は、コードの整理、見通しの良さ、保守性、チーム開発のしやすさといった点で大きなメリットがあります。

14. デバッグモード

Flaskの開発サーバーは、app.run(debug=True) のように debug=True オプションを指定することで「デバッグモード」で実行できます。デバッグモードは開発効率を大幅に向上させる便利な機能ですが、本番環境での利用は推奨されません。

デバッグモードの主な利点と注意点は以下の通りです。

  • 自動リロード: コードを変更してファイルを保存すると、サーバーが自動的に再起動されます。これにより、変更をすぐにブラウザで確認できます。
  • インタラクティブデバッガー: エラーが発生した際に、ブラウザ上でインタラクティブなデバッガーが表示されます。このデバッガーでは、スタックトレースを確認したり、変数の値を検査したり、Pythonのコードを実行したりすることができます。エラーの原因特定に非常に役立ちます。
  • 詳細なエラー出力: 通常、エラー発生時は汎用的なエラーページが表示されますが、デバッグモードではより詳細なエラー情報がブラウザに表示されます。

注意点:

  • セキュリティリスク: デバッグモードのデバッガーは非常に強力であり、ウェブブラウザ経由でサーバー上で任意のPythonコードを実行できてしまいます。本番環境で debug=True を有効にすることは、セキュリティ上の重大な脆弱性となります。 外部からの攻撃によってサーバーが乗っ取られる可能性があります。
  • パフォーマンス: デバッグモードは開発用途に特化しており、パフォーマンスは最適化されていません。
  • 本番用WSGIサーバー: Flaskの開発サーバーは、同時接続の処理や安定性などの面で本番環境には適していません。本番環境へのデプロイ時には、GunicornやuWSGIのようなプロダクショングレードのWSGIサーバーを使用する必要があります。

開発中は debug=True を有効にして効率的に作業を進め、デプロイする際には必ず debug=False にするか、環境変数などで制御するようにしてください。

15. 本番環境へのデプロイ

開発用サーバーで動作するようになったFlaskアプリケーションを、実際にインターネット経由でアクセスできるようにすることを「デプロイ」といいます。前述の通り、Flaskの開発サーバーは本番環境での利用には適していません。安定性、パフォーマンス、セキュリティの面で不足があるためです。

本番環境では、以下の要素を考慮してデプロイを行うのが一般的です。

  • WSGIサーバー: Pythonのウェブアプリケーションとウェブサーバーの間で通信を仲介するインターフェースであるWSGIを実装したサーバーが必要です。代表的なものに GunicornuWSGI があります。これらのサーバーは、複数のリクエストを同時に処理したり、安定して稼働するための機能を提供します。
  • ウェブサーバー: NginxやApacheなどのウェブサーバーをWSGIサーバーの手前に配置することがよくあります。ウェブサーバーは、静的ファイルの配信(WSGIサーバーを介さずに高速に配信)、ロードバランシング、SSL/TLS終端などの役割を担います。
  • データベース: SQLiteは小規模なアプリケーションや開発には便利ですが、本格的なアプリケーションではPostgreSQLやMySQLのようなクライアント/サーバー型のデータベースを使用するのが一般的です。
  • 環境設定: データベース接続情報、APIキー、デバッグモードの有効/無効、SECRET_KEYなどの機密情報や環境ごとの設定は、コードに直接書き込まず、環境変数などで管理するのが推奨されます。Flaskでは app.config を環境変数から読み込む仕組みを利用できます。
  • 依存関係管理: プロジェクトが必要とする全てのライブラリ(Flaskや拡張機能など)は、requirements.txt ファイルに記述しておき、デプロイ先で pip install -r requirements.txt を実行してインストールできるようにします。
  • エラー監視・ロギング: 本番環境で発生したエラーを捕捉し、ログとして記録するための仕組みが必要です。

デプロイの手順は、利用するプラットフォームや環境によって大きく異なります。代表的なデプロイ方法の概要をいくつか示します。

  • VPS (Virtual Private Server) / 専有サーバー:
    • OSをセットアップし、Python、ウェブサーバー (Nginxなど)、WSGIサーバー (Gunicornなど) をインストールします。
    • アプリケーションコードをサーバーに配置します。
    • 仮想環境を作成し、依存関係をインストールします。
    • WSGIサーバーを設定してFlaskアプリケーションを実行し、ウェブサーバーからWSGIサーバーにリクエストをプロキシするように設定します。
    • データベースサーバーをセットアップし、アプリケーションから接続できるようにします。
  • PaaS (Platform as a Service):
    • Heroku, PythonAnywhere, Google App Engine (Standard Environment), AWS Elastic Beanstalk, Azure App Service などがあります。
    • コードを特定の形式で準備し、プラットフォームにプッシュまたはアップロードします。
    • プラットフォームが自動的に環境をセットアップし、必要な依存関係をインストールし、WSGIサーバーを使ってアプリケーションを起動します。
    • 多くの場合、データベースサービスもプラットフォーム上で利用できます。
    • インフラ管理の手間が少ないのが利点ですが、プラットフォーム固有の設定や制限があります。
  • コンテナ化 (Docker):
    • アプリケーションとその依存関係をDockerイメージとしてパッケージ化します。
    • Dockerイメージをコンテナランタイム(Docker Engine, Kubernetesなど)上で実行します。
    • コンテナオーケストレーションツール(Kubernetesなど)を使えば、スケーリングや管理が容易になります。
    • デプロイ先の環境に依存しない移植性の高さが利点です。

初心者の方が手軽に試すなら、PythonAnywhereやHerokuの無料枠を利用するのがおすすめです。これらのPaaSはFlaskアプリケーションのデプロイに対応しており、インフラの構築や管理の知識がなくても比較的簡単にデプロイできます。

本番環境へのデプロイは、開発とは異なる考慮事項が多く、ウェブ開発の知識の中でもより応用的な部分になります。まずは開発環境でしっかりアプリケーションを作り込むことに集中し、その上でデプロイについて学びを進めるのが良いでしょう。

16. さらに学ぶために

この記事では、Flaskの基本的な機能を中心に解説しました。しかし、Flaskにはまだまだ多くの機能や拡張機能があり、ウェブ開発の世界は非常に広いです。さらに学びを深めるためのいくつかのステップを紹介します。

  • Flask公式ドキュメントを読む: Flaskの公式ドキュメントは非常に充実しています。基本的な使い方から高度なトピック、拡張機能の紹介まで、詳細な情報が掲載されています。困ったときはまず公式ドキュメントを確認しましょう。
  • Flaskの拡張機能を学ぶ: Flaskエコシステムには、様々な機能を提供する公式・非公式の拡張機能があります。
    • Flask-Login: ユーザー認証状態の管理。
    • Flask-WTF: フォーム処理とバリデーション(この記事でも少し紹介しました)。
    • Flask-SQLAlchemy: データベース連携(ORM)。
    • Flask-Migrate: データベーススキーマのマイグレーション(変更管理)。
    • Flask-RESTful: RESTful APIの構築支援。
    • Flask-Mail: メール送信。
    • Flask-Caching: キャッシュ機能。
    • …など、目的に応じて必要な拡張機能を学びましょう。
  • 公式チュートリアルに取り組む: Flask公式ドキュメントには、より実践的なアプリケーションを構築するステップバイステップのチュートリアルがあります。ブログアプリケーションを作成しながら、Blueprint、データベース、認証、テストなどを学ぶことができます。
  • 他のFlaskプロジェクトのコードを読む: GitHubなどで公開されている他の開発者が作ったFlaskアプリケーションのコードを読むことは、非常に良い勉強になります。様々な設計パターンやテクニックを学ぶことができます。
  • ウェブ開発全般の知識を深める: Flaskはあくまでウェブフレームワークです。HTTPプロトコル、HTML, CSS, JavaScript、データベース理論、セキュリティ(認証、認可、CSRF、XSSなど)、RESTful APIの設計原則など、ウェブ開発全般に関する知識を深めることは、より良いアプリケーションを構築するために不可欠です。
  • 自分で何か作ってみる: 学んだ知識を定着させる最も良い方法は、実際に手を動かして何かを作ってみることです。小さなブログ、ToDoリスト、ポートフォリオサイト、簡単なAPIなど、興味のあるものから始めてみましょう。

17. まとめ

この記事では、「pip install flask」から始めて、Flaskの基本的な使い方を詳細に解説しました。

  • Flaskとは何か、なぜ学習に適しているかを理解しました。
  • 仮想環境を使って開発環境を整備し、pip install Flask でFlaskをインストールしました。
  • 最小のFlaskアプリケーションを作成し、実行する方法を学びました。
  • @app.route() デコレーターを使った基本的なルーティングやURL変数の扱い方を学びました。
  • Jinja2テンプレートエンジンを使って動的なHTMLを生成し、テンプレート継承によるレイアウトの再利用を学びました。
  • 静的ファイル(CSS, JS, 画像など)の扱い方を学びました。
  • request オブジェクトを使ってクライアントからのリクエスト情報を取得し、様々な形式でレスポンスを返す方法を学びました。
  • HTMLフォームの処理と、PythonコードやFlask-WTFを使ったデータ検証(バリデーション)の基本を学びました。
  • SQLite3やFlask-SQLAlchemyを使った簡単なデータベース連携の例を見ました。
  • セッションとクッキーを使ってクライアントの状態を管理する方法を学びました。
  • エラーハンドラーを使ってカスタムエラーページを表示する方法を学びました。
  • Blueprintを使ったアプリケーションのモジュール化や、大規模なアプリケーション構成の例を紹介しました。
  • デバッグモードの便利さと本番環境での注意点を確認しました。
  • 本番環境へのデプロイについて概要を把握しました。
  • さらに学ぶためのステップを確認しました。

Flaskはシンプルでありながらも、強力なウェブアプリケーションを構築するための土台となります。この記事で学んだ基礎知識を基に、ぜひ色々なアプリケーション開発に挑戦してみてください。分からないことがあれば、公式ドキュメントを調べたり、コミュニティに質問したりしながら、一歩ずつ進んでいきましょう。

あなたのFlaskを使ったウェブ開発の旅が実り多いものとなることを願っています!

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール