PythonでWebアプリを作りたいと感じた時に、まず名前を聞くのがFlaskです。
シンプルで学びやすく、小さな個人アプリから本格的なサービスまで成長させられる柔軟さがあります。
本記事では、Flaskの基礎から環境構築、データベース連携、テスト、本番デプロイまでを一通り学べるよう、実践的なサンプルとともに丁寧に解説していきます。
なお、この記事で作成するサンプルアプリはこちらからダウンロードできます。行き詰まった際にご利用ください。
Flaskとは

Flaskの特徴とメリット
FlaskはPython製のマイクロWebフレームワークです。
「マイクロ」といっても性能が低いという意味ではなく、コア機能を最小限にし、必要な機能だけを開発者が選んで追加していく設計であることを指します。
Flaskの主な特徴として、次のような点が挙げられます。
まず、Flaskは軽量でシンプルなAPIを持ちます。
数行のコードでWebアプリを動かせるため、学習コストが非常に低いです。
初心者でも公式チュートリアルをなぞるだけで、すぐにブラウザ上にページを表示できます。
さらに、Flaskは拡張性が高いという大きなメリットがあります。
認証機能、管理画面、ORM(データベース操作)、フォームバリデーションなど、多数の拡張ライブラリが提供されており、必要になった機能だけを後から追加できます。
これにより、小さな試作から本格サービスまで、同じ技術スタックで成長させていくことが可能です。
また、Flaskはシンプルなコード構造を保ちやすい点も魅力です。
アプリの規模が小さいうちは単一ファイルでも運用でき、規模が大きくなってからパッケージ構成に分割していくことも容易です。
そのため、学習フェーズと実務フェーズの両方に適したフレームワークだと言えます。
Djangoとの違いと使い分け
PythonのWebフレームワークとして、Flaskと並んで有名なのがDjangoです。
両者の違いを理解しておくことで、プロジェクトに適した選択がしやすくなります。
Djangoは「バッテリー同梱」と表現されることが多く、認証、管理画面、ORM、テンプレートエンジンなどWeb開発に必要な多くの機能を標準で備えています。
一方で、Flaskは極めて最小限の機能のみをコアに持ち、その他は拡張として組み合わせていくスタイルです。
次の表は、FlaskとDjangoの比較を整理したものです。
| 項目 | Flask | Django |
|---|---|---|
| 性質 | マイクロフレームワーク | フルスタックフレームワーク |
| 学習ハードル | 低い | 中〜高い |
| 初期構成 | 非常にシンプル | ある程度の構造が必須 |
| 拡張 | 自由に選択して組み合わせる | 公式のやり方に沿って使う |
| 向いている用途 | プロトタイプ、小〜中規模API、柔軟な構造が必要なサービス | 大規模Webサービス、一貫したルールの必要な開発チーム |
使い分けとしては、次のように考えるとわかりやすいです。
まず、小さく始めて柔軟に拡張していきたい場合はFlaskが向いています。
APIサーバやマイクロサービス、PoC(Proof of Concept)などはFlaskとの相性が非常に良いです。
逆に、ある程度最初から要件が固まっており、大規模な開発チームで整ったルールのもと開発したい場合にはDjangoが適しています。
Flaskでできることの具体例
Flaskはシンプルでありながら、次のような多様な用途に活用できます。
まず、典型的なのはWebアプリケーションや社内ツールです。
例えばタスク管理ツール、顧客管理ツール、業務報告システムなど、フォーム入力とデータベースを組み合わせた業務アプリケーションを素早く構築できます。
次に、FlaskはREST APIサーバとしてもよく使われます。
JSONを返すエンドポイントを簡単に定義できるため、フロントエンドをReactやVueなど別技術で構築し、バックエンドAPIとしてFlaskを利用する構成にも適しています。
また、近年では機械学習モデルの推論APIや、データ分析結果を可視化する簡易ダッシュボードの構築にも広く利用されています。
Pythonで学習したモデルをそのままFlask上で読み込み、Webから推論リクエストを受け付けるという流れは、データサイエンティストにも馴染みやすいです。
開発環境構築

Pythonとpipのインストール確認
まず前提として、Python本体とパッケージ管理ツールであるpipが正しくインストールされているか確認します。
Windows、macOS、Linuxいずれの環境でも、ターミナル(またはPowerShell)を開き、次のコマンドを実行します。
python --version
python -m pip --version
あるいは、環境によってはpython3コマンドを使う場合もあります。
python3 --version
python3 -m pip --version
これらのコマンドでバージョンが表示されれば、Pythonとpipはインストール済みです。
もしコマンドが見つからない場合は、公式サイトからPythonをインストールし、PATH設定を有効にする必要があります。
仮想環境(venv)の作成と有効化
Flaskを含むPythonパッケージは、プロジェクトごとに仮想環境を作って管理することが推奨されます。
これにより、別プロジェクト同士で依存パッケージのバージョンが衝突することを防げます。
まず、Flaskプロジェクト用のディレクトリを作成し、その中で仮想環境を作ります。
# プロジェクト用ディレクトリの作成と移動
mkdir flask-tutorial
cd flask-tutorial
# venvという名前の仮想環境を作成
python -m venv venv
次に、この仮想環境を有効化します。
OSごとにコマンドが異なります。
# Windows (PowerShell)
.\venv\Scripts\Activate.ps1
# Windows (コマンドプロンプト)
venv\Scripts\activate.bat
# macOS / Linux (bash, zsh など)
source venv/bin/activate
有効化に成功すると、ターミナルの先頭に(venv)のような表示が追加されます。
これ以降は、(venv)が付いた状態でFlaskなどのパッケージをインストールしていきます。
Flaskのインストールとバージョン確認
仮想環境が有効化された状態で、pipを使ってFlaskをインストールします。
# Flaskのインストール
pip install Flask
インストール後、バージョンを確認しておくとよいでしょう。
python -c "import flask; print(flask.__version__)"
出力されたバージョンをメモしておくと、後から環境を再現したい時に役立ちます。
また、依存関係をrequirements.txtとして保存する場合は次のようにします。
pip freeze > requirements.txt
このファイルは、別の環境で同じパッケージ群を復元する際に利用できます。
VS CodeなどIDEの設定ポイント
Flask開発には、VS Codeのようなモダンなエディタを利用すると効率が上がります。
ここではVS Codeを例に、設定のポイントを説明します。
まず、VS CodeでPython拡張機能をインストールします。
これにより、Python用のシンタックスハイライト、補完、デバッグ機能が追加されます。
続いて、コマンドパレットからPython: Select Interpreterを選択し、先ほど作成した仮想環境venvのPythonインタプリタを選びます。
これにより、VS Code内で実行されるPythonが仮想環境のものになります。
次に、Flaskアプリのデバッグ設定を行います。
VS Codeの「実行とデバッグ」から構成ファイル.vscode/launch.jsonを作成し、Flask用の設定を追加します。
例えば次のような内容です。
{
// .vscode/launch.json
"version": "0.2.0",
"configurations": [
{
"name": "Flask: Debug",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "app.py",
"FLASK_ENV": "development"
},
"args": [
"run",
"--no-debugger",
"--no-reload"
],
"jinja": true
}
]
}
この設定により、VS Codeからブレークポイントを置いてFlaskアプリをデバッグ実行できるようになります。
初学者のうちは細かい設定にこだわる必要はありませんが、仮想環境のインタプリタを選ぶことと、FLASK_APP環境変数を正しく指定することだけは意識しておくとよいです。
最初のFlaskアプリ作成

最小構成のFlaskアプリ
まずは、最小限のFlaskアプリを1ファイルで作成してみます。
プロジェクトフォルダflask-tutorialの中にapp.pyというファイルを作成し、次のコードを書き込みます。
# app.py
from flask import Flask
# Flaskアプリケーションのインスタンスを作成
# __name__ は現在のモジュール名が入る特別な変数
app = Flask(__name__)
# ルートURL("/")にアクセスが来たときに呼ばれる関数を定義
@app.route("/")
def hello():
# ブラウザに返す文字列をreturnで指定
return "Hello, Flask!"
このファイルだけで、最も基本的なFlaskアプリが完成します。
ここで重要なのは、Flaskインスタンスを生成し、ルートをデコレータ@app.routeで定義している点です。
Flaskアプリの実行方法
次に、このアプリを実行してブラウザからアクセスしてみます。
ターミナルで仮想環境(venv)が有効化されていることを確認し、次のコマンドを実行します。
# Windows / macOS / Linux 共通
set FLASK_APP=app.py # Windows(PowerShellなら $env:FLASK_APP="app.py")
flask run
ただし、WindowsとUnix系で環境変数の設定方法が異なるため、実際には次のように使い分けます。
# Windows (PowerShell)
$env:FLASK_APP = "app.py"
flask run
# Windows (コマンドプロンプト)
set FLASK_APP=app.py
flask run
# macOS / Linux
export FLASK_APP=app.py
flask run
コマンドを実行すると、ターミナルにサーバ起動のログが表示されます。
* Serving Flask app 'app.py'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
ブラウザでhttp://127.0.0.1:5000/(またはhttp://localhost:5000/)にアクセスすると、「Hello, Flask!」という文字列が表示されるはずです。
ポート番号とデバッグモードの設定
Flask開発では、デバッグモードを有効にすると自動リロードやエラーページが便利になります。
また、ポート番号を変更したい場合もよくあります。
これらはflask runコマンドのオプションや環境変数で制御できます。
# デバッグモードを有効化して起動
# Windows (PowerShell)
$env:FLASK_APP = "app.py"
$env:FLASK_ENV = "development"
flask run
# macOS / Linux
export FLASK_APP=app.py
export FLASK_ENV=development
flask run
あるいは、コマンドライン引数でポートを指定することもできます。
# ポート番号を8000番に変更して起動
flask run --port 8000
アプリ側のコードで直接実行する方法もあります。
例えば次のようにapp.run()を使うと、Pythonスクリプトとして起動できます。
# app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello, Flask!"
# Pythonスクリプトとして直接実行された時だけ起動する
if __name__ == "__main__":
# debug=True でデバッグモードを有効化
app.run(debug=True, port=5000)
# 実行コマンド例
python app.py
本番環境ではFlaskの開発用サーバをそのまま使わないことが重要です。
後述するWSGIサーバ(gunicornなど)を利用するのが一般的です。
ルーティングとビューの基本

ルート定義(route)の書き方
Flaskでは、@app.routeデコレータを使ってURLとPython関数を紐付けることでルーティングを実現します。
先ほどの最小アプリに、いくつかルートを追加してみましょう。
# app.py
from flask import Flask
app = Flask(__name__)
# トップページ
@app.route("/")
def index():
return "トップページです"
# /about へのルート
@app.route("/about")
def about():
return "このサイトについて"
# 複数のURLに同じ関数を割り当てることも可能
@app.route("/hello")
@app.route("/hi")
def greet():
return "こんにちは!"
このように、デコレータ@app.route("/path")を関数の直前に付けることで、その関数が特定パスへのリクエスト処理を担当するようになります。
1つの関数に複数のパスを対応させることも可能で、リダイレクトや互換URLのために使われることがあります。
パスパラメータとクエリパラメータ
URLには、パスパラメータとクエリパラメータの2種類の情報の渡し方があります。
Flaskでは、パスパラメータはルート定義の中で扱い、クエリパラメータはリクエストオブジェクトから取得します。
まず、パスパラメータの例です。
# app.py の一部
from flask import Flask
app = Flask(__name__)
# /users/123 のように、ユーザID部分を変数として受け取る
@app.route("/users/<int:user_id>")
def show_user(user_id):
# user_id は int 型として渡される
return f"ユーザID: {user_id}"
# 型指定をしない場合は文字列として扱われる
@app.route("/articles/<slug>")
def show_article(slug):
# 例: /articles/flask-intro
return f"記事スラッグ: {slug}"
一方、クエリパラメータは?key=valueの形式でURL末尾に付与されます。
例えば/search?q=flask&page=2のような形です。
Flaskではrequest.argsから取得します。
# app.py の一部
from flask import Flask, request
app = Flask(__name__)
@app.route("/search")
def search():
# クエリパラメータ q と page を取得 (存在しない場合はNone)
keyword = request.args.get("q")
page = request.args.get("page", default=1, type=int)
return f"検索キーワード: {keyword}, ページ: {page}"
パスパラメータは「リソースの場所」そのものを表すのに向いており、クエリパラメータは「検索条件やページング情報」といった補助的な情報に向いています。
API設計では、この使い分けを意識することが重要です。
HTTPメソッド(GETとPOST)の扱い
HTTPリクエストには、GET、POST、PUT、DELETEなど複数のメソッドがあります。
Flaskでは、@app.routeの引数methodsで受け付けるメソッドを指定します。
最も基本的なGETとPOSTの扱い方を見てみましょう。
# app.py の一部
from flask import Flask, request
app = Flask(__name__)
# GETメソッドのみを受け付ける (デフォルト)
@app.route("/hello_get")
def hello_get():
return "これはGET専用エンドポイントです"
# GETとPOSTの両方に対応
@app.route("/hello_form", methods=["GET", "POST"])
def hello_form():
if request.method == "POST":
# フォームから送信されたnameを取得
name = request.form.get("name", "名無し")
return f"こんにちは、{name}さん!"
else:
# GET時はフォームを表示
return """
<form method="post">
名前: <input type="text" name="name">
<input type="submit" value="送信">
</form>
"""
このように、request.methodで現在のHTTPメソッドを確認し、処理を分岐させます。
GETは主にデータ取得、POSTはデータ登録・更新など副作用のある処理に使うというのが一般的なRESTの設計方針です。
テンプレートとHTML表示

Jinja2テンプレートの基本構造
FlaskはJinja2というテンプレートエンジンを標準で利用します。
ビュー関数からHTMLを返す際、テンプレートファイルをレンダリングすることで、Python側で用意したデータをHTMLに埋め込むことができます。
まず、プロジェクト内にtemplatesディレクトリを作り、その中にindex.htmlを配置します。
flask-tutorial/
app.py
venv/
templates/
index.html
index.htmlの例は次の通りです。
<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Flask入門</title>
</head>
<body>
<h1>Flaskへようこそ</h1>
<p>このページはテンプレートからレンダリングされています。</p>
</body>
</html>
ビュー関数では、render_template関数を使ってテンプレートをレンダリングします。
# app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
# templates/index.html をレンダリングし、その結果のHTMLを返す
return render_template("index.html")
これで/にアクセスすると、先ほどのHTMLがブラウザに表示されます。
Flaskはtemplatesディレクトリを自動的にテンプレート探索パスとして認識するため、特別な設定は不要です。
変数展開と制御構文
Jinja2テンプレートでは、{{ 変数 }}の構文で変数を埋め込み、{% … %}で制御構文(if, forなど)を記述します。
# app.py の一部
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/hello/<name>")
def hello(name):
# テンプレートに name と fruits という変数を渡す
fruits = ["りんご", "バナナ", "オレンジ"]
return render_template("hello.html", name=name, fruits=fruits)
<!-- templates/hello.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>{{ name }} さん、こんにちは</title>
</head>
<body>
<h1>{{ name }} さん、ようこそ!</h1>
<!-- if文による条件分岐 -->
{% if fruits %}
<p>好きなフルーツ一覧:</p>
<ul>
<!-- for文による繰り返し -->
{% for fruit in fruits %}
<li>{{ fruit }}</li>
{% endfor %}
</ul>
{% else %}
<p>フルーツ情報はありません。</p>
{% endif %}
</body>
</html>
上記テンプレートでは、ビュー関数から渡されたnameとfruitsを表示しています。
{% if fruits %}のように、Pythonに近い構文で条件分岐やループが書けるため、HTMLとロジックのバランスよい分離が可能です。
テンプレート継承と共通レイアウト
複数ページを持つアプリケーションでは、ヘッダやフッタなど共通部分をテンプレート継承でまとめるのが一般的です。
Jinja2ではblockとextendsを使ってこれを実現します。
まず、共通レイアウトとなるbase.htmlを用意します。
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>{% block title %}Flaskサンプルサイト{% endblock %}</title>
</head>
<body>
<header>
<h1>Flaskサンプルサイト</h1>
<nav>
<a href="{{ url_for('index') }}">Home</a> |
<a href="{{ url_for('about') }}">About</a>
</nav>
<hr>
</header>
<main>
<!-- 各ページごとに変わる部分 -->
{% block content %}{% endblock %}
</main>
<footer>
<hr>
<p>© 2025 My Flask Site</p>
</footer>
</body>
</html>
次に、このレイアウトを継承して各ページのテンプレートを作ります。
<!-- templates/index.html -->
{% extends "base.html" %}
{% block title %}トップページ{% endblock %}
{% block content %}
<h2>トップページ</h2>
<p>これはトップページの内容です。</p>
{% endblock %}
<!-- templates/about.html -->
{% extends "base.html" %}
{% block title %}このサイトについて{% endblock %}
{% block content %}
<h2>このサイトについて</h2>
<p>Flaskの学習用サンプルサイトです。</p>
{% endblock %}
# app.py の一部
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/about")
def about():
return render_template("about.html")
このように、共通レイアウトを1箇所にまとめておくことで、デザイン変更が容易になり、コードの重複も大幅に減らせます。
フォーム処理とバリデーション

HTMLフォーム送信とPOST処理
Flaskでフォームを扱う基本パターンは、GETでフォームを表示し、POSTで送信されたデータを処理するという流れです。
先ほどのテンプレートとビューをもう少し整理した例を示します。
# app.py の一部
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route("/contact", methods=["GET", "POST"])
def contact():
if request.method == "POST":
# フォームから送信された値を取得
name = request.form.get("name")
message = request.form.get("message")
# ここで簡易なチェック(本格的なバリデーションは後述)
if not name or not message:
error = "名前とメッセージは必須です。"
# エラー時は同じテンプレートにエラーメッセージを渡して再表示
return render_template("contact.html", error=error, name=name, message=message)
# 通常はここでメール送信やDB保存などを行う
success_message = "お問い合わせを受け付けました。ありがとうございます。"
return render_template("contact_result.html", success_message=success_message)
# GETメソッド時はフォームを表示
return render_template("contact.html")
<!-- templates/contact.html -->
{% extends "base.html" %}
{% block title %}お問い合わせ{% endblock %}
{% block content %}
<h2>お問い合わせフォーム</h2>
{% if error %}
<p style="color:red;">{{ error }}</p>
{% endif %}
<form method="post">
<div>
<label>お名前:</label>
<input type="text" name="name" value="{{ name or '' }}">
</div>
<div>
<label>メッセージ:</label>
<textarea name="message">{{ message or '' }}</textarea>
</div>
<button type="submit">送信</button>
</form>
{% endblock %}
<!-- templates/contact_result.html -->
{% extends "base.html" %}
{% block title %}お問い合わせ完了{% endblock %}
{% block content %}
<h2>送信完了</h2>
<p>{{ success_message }}</p>
{% endblock %}
このように、同じURLでGETとPOSTを切り替えながらフォーム表示と送信処理を行うのが一般的なパターンです。
フォームデータの取得
フォームから送信されたデータは、Flaskのrequest.formから取得します。
これは辞書のように振る舞うオブジェクトです。
また、チェックボックスや複数選択の値を取得する場合はgetlistを使います。
# app.py の一部
from flask import Flask, request
app = Flask(__name__)
@app.route("/survey", methods=["POST"])
def survey():
# 単一値の取得
age = request.form.get("age")
gender = request.form.get("gender")
# 複数選択(チェックボックスなど)の取得
interests = request.form.getlist("interests")
return f"年齢: {age}, 性別: {gender}, 興味: {', '.join(interests)}"
基本的にはrequest.form.get("フィールド名")で値を取得し、必須チェックなどを行います。
ただし、手動でバリデーションを実装すると煩雑になりやすいため、より本格的なアプリケーションではWTFormsなどのライブラリを利用することが多いです。
WTFormsを使った入力チェック
Flaskでは、Flask-WTFという拡張を使用することで、WTFormsベースのフォーム処理とバリデーションを簡潔に記述できます。
ここでは典型的な利用例を紹介します。
まず、必要なパッケージをインストールします。
pip install flask-wtf wtforms
次に、アプリにシークレットキーを設定し、フォームクラスを定義します。
# app.py
from flask import Flask, render_template, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField
from wtforms.validators import DataRequired, Length
app = Flask(__name__)
# CSRF対策などに必須となるシークレットキー
app.config["SECRET_KEY"] = "change_this_to_random_string"
# WTFormsのフォームクラスを定義
class ContactForm(FlaskForm):
name = StringField(
"お名前",
validators=[DataRequired(message="名前は必須です。"), Length(max=50)]
)
message = TextAreaField(
"メッセージ",
validators=[DataRequired(message="メッセージは必須です。"), Length(max=500)]
)
submit = SubmitField("送信")
@app.route("/contact_wtf", methods=["GET", "POST"])
def contact_wtf():
form = ContactForm()
if form.validate_on_submit():
# バリデーション成功時の処理
name = form.name.data
message = form.message.data
# ここでメール送信やDB保存などを行う想定
return redirect(url_for("contact_wtf_done", name=name))
# GET時またはバリデーションエラー時
return render_template("contact_wtf.html", form=form)
@app.route("/contact_wtf/done")
def contact_wtf_done():
name = request.args.get("name", "お客様")
return f"{name} さん、お問い合わせありがとうございました。"
<!-- templates/contact_wtf.html -->
{% extends "base.html" %}
{% block title %}お問い合わせ(WTForms版){% endblock %}
{% block content %}
<h2>お問い合わせフォーム(WTForms)</h2>
<!-- CSRFトークンを自動埋め込み -->
<form method="post">
{{ form.hidden_tag() }}
<div>
{{ form.name.label }}<br>
{{ form.name(size=40) }}
{% for error in form.name.errors %}
<span style="color:red;">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.message.label }}<br>
{{ form.message(rows=5, cols=40) }}
{% for error in form.message.errors %}
<span style="color:red;">{{ error }}</span>
{% endfor %}
</div>
<div>
{{ form.submit() }}
</div>
</form>
{% endblock %}
WTFormsを使うことで、バリデーションルールをフォームクラス内に宣言的に記述でき、テンプレート側ではエラーメッセージを簡潔に表示できます。
これにより、フォームが増えてもコードの整理がしやすくなります。
静的ファイルとディレクトリ構成

staticディレクトリとCSS・JSの配信
Flaskでは、staticディレクトリ以下が静的ファイル(画像、CSS、JavaScriptなど)の配置場所として扱われます。
標準では、プロジェクト直下のstaticフォルダが対象です。
ディレクトリ構成は次のようになります。
flask-tutorial/
app.py
templates/
base.html
index.html
static/
css/
style.css
js/
app.js
images/
logo.png
テンプレートから静的ファイルへのURLを生成する場合は、url_for(“static”, filename=”パス”)を利用します。
<!-- templates/base.html の一部 -->
<head>
<meta charset="UTF-8">
<title>{% block title %}Flaskサイト{% endblock %}</title>
<!-- CSSファイルを読み込む -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<!-- JavaScriptファイルを読み込む -->
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
</body>
/* static/css/style.css */
body {
font-family: sans-serif;
line-height: 1.6;
}
h1 {
color: #333;
}
こうすることで、Flaskの開発サーバが/static/...というURLパスで静的ファイルを配信してくれます。
本番環境では、一般的にWebサーバ(NginxやApache)が静的ファイルを直接配信する設定にします。
Flask推奨のプロジェクト構成
アプリが大きくなってきた場合、1ファイル構成からパッケージ構成へ整理することが重要です。
Flaskは柔軟なため様々な構成を取れますが、代表的な一例を示します。
myflaskapp/
app/
__init__.py
routes.py
models.py
forms.py
templates/
base.html
index.html
static/
css/
js/
migrations/
tests/
config.py
wsgi.py
requirements.txt
この構成では、appパッケージの中にルーティング、モデル、フォーム、テンプレートなどをまとめています。
__init__.pyでアプリケーションファクトリを定義し、wsgi.pyからそれを読み込んでWSGIサーバに渡す、というのが典型的なパターンです。
configファイルと環境別設定
Flaskアプリでは、開発用・テスト用・本番用で設定を分けることがよく行われます。
例えばデバッグモードやデータベースURLなどが環境ごとに異なります。
# config.py
import os
class Config:
SECRET_KEY = os.environ.get("SECRET_KEY") or "dev_key_change_later"
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL") or "sqlite:///app.db"
SQLALCHEMY_TRACK_MODIFICATIONS = False
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
# app/__init__.py
from flask import Flask
from .extensions import db # SQLAlchemyなど拡張をまとめる想定
from . import routes
def create_app(config_class=None):
app = Flask(__name__)
# 環境変数 FLASK_CONFIG に応じて設定を切り替える例
if config_class is None:
env = os.environ.get("FLASK_CONFIG", "development")
if env == "production":
from config import ProductionConfig
config_class = ProductionConfig
elif env == "testing":
from config import TestingConfig
config_class = TestingConfig
else:
from config import DevelopmentConfig
config_class = DevelopmentConfig
app.config.from_object(config_class)
# 拡張の初期化
db.init_app(app)
# ルートの登録
app.register_blueprint(routes.bp)
return app
このように、設定をクラスとして分割し、環境変数などでどの設定を使うか切り替えることで、本番環境と開発環境の違いを管理しやすくなります。
データベース連携

SQLiteとSQLAlchemyの導入
Flaskでは、SQLAlchemyというORM(Object Relational Mapper)を利用してデータベース操作を簡易化するのが一般的です。
SQLiteはファイルベースの軽量DBで、学習や小規模アプリに適しています。
まず、必要なパッケージをインストールします。
pip install flask-sqlalchemy
次に、Flaskアプリにデータベース設定を追加します。
# app.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# SQLiteデータベースファイル app.db を利用
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
モデル定義とテーブル作成
SQLAlchemyでは、Pythonクラスを定義することでテーブル構造を表現します。
ここでは簡単な「Todo」モデルを例にしてみます。
# app.py の続き
from datetime import datetime
class Todo(db.Model):
__tablename__ = "todos" # テーブル名を明示的に指定
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
is_done = db.Column(db.Boolean, default=False, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return f"<Todo id={self.id} title={self.title!r}>"
# 初回のみテーブルを作成
if __name__ == "__main__":
with app.app_context():
db.create_all()
print("テーブルを作成しました。")
# 実行例
python app.py
# 出力例
テーブルを作成しました。
db.create_all()が呼ばれると、定義されたモデルに対応するテーブルがデータベース内に作成されます。
SQLiteを利用している場合は、カレントディレクトリにapp.dbというファイルが生成されます。
CRUD処理の実装例
モデルを定義したら、次にCRUD(Create/Read/Update/Delete)の基本操作を実装してみます。
ここでは、簡単なTodoリストアプリを作成します。
# app.py 全体例 (簡易Todoアプリ)
from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///app.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
class Todo(db.Model):
__tablename__ = "todos"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
is_done = db.Column(db.Boolean, default=False, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
@app.route("/")
def index():
# 未完了のTodoを新しい順に一覧表示
todos = Todo.query.order_by(Todo.created_at.desc()).all()
return render_template("todo_list.html", todos=todos)
@app.route("/add", methods=["POST"])
def add():
title = request.form.get("title")
if not title:
return redirect(url_for("index"))
todo = Todo(title=title)
db.session.add(todo)
db.session.commit()
return redirect(url_for("index"))
@app.route("/done/<int:todo_id>")
def done(todo_id):
todo = Todo.query.get_or_404(todo_id)
todo.is_done = True
db.session.commit()
return redirect(url_for("index"))
@app.route("/delete/<int:todo_id>")
def delete(todo_id):
todo = Todo.query.get_or_404(todo_id)
db.session.delete(todo)
db.session.commit()
return redirect(url_for("index"))
if __name__ == "__main__":
with app.app_context():
db.create_all()
app.run(debug=True)
<!-- templates/todo_list.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Todoリスト</title>
</head>
<body>
<h1>Todoリスト</h1>
<form action="{{ url_for('add') }}" method="post">
<input type="text" name="title" placeholder="新しいタスクを入力">
<button type="submit">追加</button>
</form>
<ul>
{% for todo in todos %}
<li>
{% if todo.is_done %}
<del>{{ todo.title }}</del>
{% else %}
{{ todo.title }}
<a href="{{ url_for('done', todo_id=todo.id) }}">完了</a>
{% endif %}
<a href="{{ url_for('delete', todo_id=todo.id) }}">削除</a>
</li>
{% else %}
<li>タスクはまだありません。</li>
{% endfor %}
</ul>
</body>
</html>
この例では、db.sessionを通じてレコードの追加・更新・削除を行い、クエリAPIでデータ取得を行っている点がポイントです。
本格的なアプリでは、さらにリポジトリ層やサービス層を設けて責務を分離する場合もありますが、まずはこのレベルでCRUDの流れを理解すると良いです。
Flaskアプリのテストとデバッグ

ログ出力とデバッグツールの活用
Flaskアプリの開発では、ログを適切に出力しておくことが重要です。
ログは開発時のデバッグだけでなく、本番運用時のトラブルシューティングにも役立ちます。
# app.py の一部
import logging
from flask import Flask
app = Flask(__name__)
# ロガーの設定
logging.basicConfig(level=logging.INFO)
@app.route("/debug-example")
def debug_example():
app.logger.info("debug-example にアクセスがありました。")
app.logger.warning("これは警告ログの例です。")
return "ログ出力のサンプル"
デフォルトでは標準出力にログが出ますが、本番環境ではファイルや外部ログサービスに出力することが一般的です。
また、開発時にはFlask-DebugToolbarのような拡張を使うと、SQLクエリやテンプレート情報などをブラウザで可視化できます。
pip install flask-debugtoolbar
# app.py の一部
from flask_debugtoolbar import DebugToolbarExtension
app = Flask(__name__)
app.config["SECRET_KEY"] = "dev"
app.config["DEBUG_TB_INTERCEPT_REDIRECTS"] = False
toolbar = DebugToolbarExtension(app)
unittestやpytestによるテスト
Flaskはテストクライアントを提供しており、HTTPリクエストを擬似的に送ってレスポンスを検証することができます。
標準ライブラリのunittestを使った例を示します。
# test_app.py
import unittest
from app import app # app.py で作成した Flask インスタンスをインポート
class FlaskAppTestCase(unittest.TestCase):
def setUp(self):
# テストクライアントの作成
self.app = app.test_client()
self.app.testing = True
def test_index_status_code(self):
# GET / にアクセスしてステータスコードを確認
response = self.app.get("/")
self.assertEqual(response.status_code, 200)
def test_index_content(self):
response = self.app.get("/")
self.assertIn(b"トップページ", response.data)
if __name__ == "__main__":
unittest.main()
# 実行例
python -m unittest test_app.py
より柔軟で記述量の少ないテストを書きたい場合は、pytestの利用が一般的です。
fixturesを使ってアプリコンテキストやDBのセットアップ・クリーンアップを自動化することもできます。
エラーハンドリングと例外処理
本番運用を見据えたFlaskアプリでは、エラー発生時にユーザ向けのわかりやすい画面を表示し、詳細なスタックトレースはログにのみ出力する設計が重要です。
Flaskでは@app.errorhandlerデコレータを使ってエラーハンドラを定義できます。
# app.py の一部
from flask import render_template
@app.errorhandler(404)
def page_not_found(e):
# 404.html テンプレートを表示
return render_template("404.html"), 404
@app.errorhandler(500)
def internal_server_error(e):
# ログに詳細を出力
app.logger.error("サーバ内部エラー", exc_info=e)
return render_template("500.html"), 500
<!-- templates/404.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>ページが見つかりません</title>
</head>
<body>
<h1>404 - ページが見つかりません</h1>
<p>お探しのページは削除されたか、移動した可能性があります。</p>
</body>
</html>
アプリケーションコード内では、try/exceptで適切に例外を捕捉し、必要に応じてabort(404)などの関数でHTTPエラーを発生させることもできます。
Flaskアプリの本番運用準備

開発環境と本番環境の違い
Flaskには組み込みの開発用サーバがありますが、これは本番環境での利用は想定されていません。
本番では、専用のWSGIサーバを用いてFlaskアプリを起動し、Webサーバ(NginxやApache)と連携させるのが一般的です。
主な違いとしては、次の点が挙げられます。
まず、本番環境ではDEBUGを必ずFalseにする必要があります。
デバッグモードを有効にすると、エラー時にスタックトレースや環境情報がブラウザに表示されてしまい、セキュリティリスクになります。
また、本番ではホットリロードやDebugToolbarのような開発支援機能は無効化します。
さらに、本番環境では複数プロセス・スレッドによる並行処理、ログローテーション、リソース制限などを運用設計に含める必要があります。
この役割を担うのがWSGIサーバとWebサーバです。
WSGIアプリケーションとしてのFlask
FlaskはWSGI(Web Server Gateway Interface)というPython標準のWebアプリケーションインターフェース仕様に準拠しています。
Flaskアプリは、内部的にはapp(environ, start_response)という形の呼び出し可能オブジェクトとして動作します。
本番環境での起動では、一般的にwsgi.pyというエントリーポイントを用意し、その中でFlaskアプリを作成します。
# wsgi.py
from app import create_app # アプリケーションファクトリを想定
# WSGIサーバ(gunicorn, uWSGIなど)が読み込むエントリーポイント
app = create_app()
WSGIサーバは、このappオブジェクトを読み込み、HTTPリクエストを渡してレスポンスを受け取り、Webサーバと連携してクライアントに返します。
gunicornやuWSGIの導入ポイント
Linux環境などでFlaskアプリを本番運用する場合、gunicornやuWSGIがよく使われます。
ここではgunicornを例に基本的な使い方を説明します。
# インストール
pip install gunicorn
次に、Flaskアプリをgunicornで起動します。
# wsgi:app という表記は「wsgi.py の中の app という変数」を意味する
gunicorn wsgi:app --bind 0.0.0.0:8000 --workers 3
このコマンドにより、ポート8000番で3ワーカー構成のアプリサーバが立ち上がります。
本番運用では、ワーカー数、タイムアウト、ログファイル位置などを設定ファイルで細かく調整することが多いです。
uWSGIも同様にWSGIアプリを起動できますが、設定オプションが非常に豊富なため、公式ドキュメントや実運用事例を参考にしながら導入することが重要です。
Flaskアプリのデプロイ
※構成上、同名の見出しが2回ありますが、本セクションでは主にLinuxサーバやオンプレ環境向けの手順を扱います。

LinuxサーバへのFlaskデプロイ手順
一般的なLinuxサーバ(Ubuntuなど)へのデプロイ手順は、次の流れになります。
- Pythonとpip、必要ならgitのインストール
- プロジェクトコードの配置(デプロイツールやgit cloneなど)
- 仮想環境の作成とパッケージインストール
- WSGIサーバ(gunicornなど)の起動確認
- Webサーバ(Nginx/Apache)との連携設定
- systemdによる常駐化と自動起動設定
ここでは概要のみを押さえ、詳細は後続の各項目で説明していきます。
ApacheまたはNginxとの連携設定
本番環境では、Flaskアプリの前段にWebサーバを配置します。
これには次のような理由があります。
まず、静的ファイルの高速配信をWebサーバに任せられること、そしてHTTPS終端(SSL/TLS)やリバースプロキシ、多様なリライトルールのサポートが挙げられます。
Nginxを例にした基本的な設定は次のようになります。
# /etc/nginx/sites-available/myflaskapp.conf の例
server {
listen 80;
server_name example.com;
# 静的ファイルの配信
location /static/ {
alias /var/www/myflaskapp/app/static/;
}
# Flaskアプリへのリバースプロキシ
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# 設定反映の一例(Ubuntu)
sudo ln -s /etc/nginx/sites-available/myflaskapp.conf /etc/nginx/sites-enabled/
sudo nginx -t # 設定テスト
sudo systemctl reload nginx
この構成では、Nginxが80番ポートでリクエストを受け付け、内部のgunicorn(127.0.0.1:8000)にリバースプロキシします。
systemdによる常駐起動とログ管理
Linuxサーバでプロセスを常駐させ、自動起動させるにはsystemdのサービスユニットを定義するのが一般的です。
gunicornをsystemdで管理する設定例を示します。
# /etc/systemd/system/myflaskapp.service
[Unit]
Description=Gunicorn instance to serve myflaskapp
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myflaskapp
Environment="PATH=/var/www/myflaskapp/venv/bin"
ExecStart=/var/www/myflaskapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 wsgi:app
# 自動再起動などの設定
Restart=always
[Install]
WantedBy=multi-user.target
# サービスの有効化と起動
sudo systemctl daemon-reload
sudo systemctl enable myflaskapp
sudo systemctl start myflaskapp
# 状態とログの確認
sudo systemctl status myflaskapp
journalctl -u myflaskapp -f
systemdでプロセスを管理することで、サーバ再起動時の自動起動、プロセス異常終了時の再起動、ログの集中管理などが容易になります。
Flaskアプリのデプロイ(クラウド・コンテナ)
同名の見出しですが、こちらではクラウドサービスやコンテナを使ったデプロイ方法を解説します。

Python FlaskとAWS(EBS, EC2)の連携
AWSでFlaskアプリを動かす方法はいくつかありますが、代表的なのはElastic Beanstalk(EBS)とEC2です。
Elastic Beanstalkは、アプリケーションをアップロードするだけで、EC2インスタンス、ロードバランサ、オートスケーリング、ログ管理などを自動的に構成してくれるサービスです。
FlaskアプリをEBSにデプロイするには、ざっくり次の流れになります。
- AWSアカウントとAWS CLIの設定
- プロジェクトルートに
requirements.txtとapplication.py(またはwsgi.py)を用意 - Elastic Beanstalk CLIでアプリケーションを初期化
eb deployコマンドでデプロイ
一方、EC2を使う場合は、前述したLinuxサーバへのデプロイ手順と同様に、自分でWSGIサーバとNginxを構成します。
自由度は高いですが、運用負荷も増えるため、自動化ツール(AnsibleやTerraformなど)と組み合わせるのが一般的です。
Python FlaskとHerokuのデプロイ手順
Herokuは、アプリケーションのコードをpushするだけでデプロイできるPaaSとして人気があります。
小中規模のFlaskアプリには非常に相性が良いです。
代表的な手順は次の通りです。
- HerokuアカウントとHeroku CLIの準備
- プロジェクトに
Procfileとrequirements.txtを追加 - gitリポジトリを作成し、Herokuにpush
# Procfile の例
web: gunicorn wsgi:app
# デプロイの流れ例
heroku login
heroku create my-flask-app
git init
git add .
git commit -m "first deploy"
git push heroku main
Herokuは、環境変数の管理やログ閲覧、スケールアウトなどが簡単に行えるため、学習用・試作用のデプロイ先としても便利です。
DockerでFlaskをコンテナデプロイ
近年は、DockerコンテナにFlaskアプリをパッケージングし、任意の環境にデプロイする方法が主流になりつつあります。
Dockerを使うと、「ローカルと本番で動作環境が違う」という問題を大幅に減らせます。
簡単なDockerfileの例を示します。
# Dockerfile
FROM python:3.11-slim
# 作業ディレクトリを作成
WORKDIR /app
# 依存パッケージをコピーしてインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションコードをコピー
COPY . .
# 環境変数 (本番では別途設定推奨)
ENV FLASK_ENV=production
# コンテナ起動時に実行するコマンド
CMD ["gunicorn", "wsgi:app", "--bind", "0.0.0.0:8000"]
# イメージのビルドとコンテナ起動例
docker build -t myflaskapp .
docker run -p 8000:8000 myflaskapp
Docker Composeを利用すれば、アプリコンテナとDBコンテナ(PostgreSQLなど)を同時に立ち上げる構成も容易に構築できます。
クラウド側は、AWS ECS/FargateやKubernetesなどのコンテナオーケストレーション基盤を組み合わせて運用することが多いです。
セキュリティと運用の基本

環境変数とシークレットキー管理
Flaskアプリでは、SECRET_KEYやDBのパスワード、APIキーなど、機微情報を安全に管理することが欠かせません。
これらをソースコードにベタ書きするのは非常に危険です。
代わりに、環境変数を通じて設定を行い、config.pyなどで参照するのが一般的です。
# config.py の一部
import os
class Config:
SECRET_KEY = os.environ.get("SECRET_KEY")
DATABASE_URL = os.environ.get("DATABASE_URL")
# Linux / macOS の例
export SECRET_KEY="本番用のランダムな文字列"
export DATABASE_URL="postgresql://user:pass@host/dbname"
本番環境では、OSやクラウドサービス(AWS Systems Manager Parameter Store、Secrets Managerなど)が提供する仕組みを利用して、暗号化された形でシークレットを保管し、アプリ起動時に環境変数として注入する方法が推奨されます。
CSRF対策とセッション管理
フォームを扱うWebアプリでは、CSRF(Cross-Site Request Forgery)対策が必須です。
Flask-WTFを利用している場合は、CSRFトークンが自動的にフォームに埋め込まれ、サーバ側で検証されます。
# app.py の一部 (Flask-WTF利用時)
app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY")
# FlaskForm クラスを使うだけで CSRF トークンが自動付与される
<!-- テンプレート側で必ず hidden_tag() を呼ぶ -->
<form method="post">
{{ form.hidden_tag() }}
<!-- 他のフィールド -->
</form>
セッション管理については、Flaskはクッキーに署名付きのセッションデータを保存する仕組みを持っています。
SECRET_KEYが漏洩するとセッション改ざんのリスクが高まるため、前述の通り厳重な管理が必要です。
また、ログイン機能などを実装する場合は、Flask-Loginのような拡張を利用して、安全なログインセッション管理を行うのが一般的です。
Flaskアプリのログ監視と運用改善
本番運用では、ログを集約し、監視・分析する仕組みが重要です。
例えば、次のような指標を定期的に確認することで、問題の早期発見や改善に繋がります。
まず、HTTPステータスコードの分布を監視し、5xxエラーの急増や特定URLでの4xxエラー多発などを検知します。
また、アプリケーションログから例外発生数や特定機能の利用状況を集計し、改善の優先度付けに役立てます。
ログの集中管理には、ELKスタック(Elasticsearch + Logstash + Kibana)やCloudWatch Logs、Datadogなどのサービスがよく利用されます。
これらと連携して、閾値を超えたエラー発生時にアラートを飛ばす仕組みを整えておくと、障害対応の初動を早めることができます。
完成したプロジェクトはこちらからダウンロードできます。
まとめ
Flaskは、小さなスクリプトから本格的なWebサービスまで、段階的にスケールさせていける柔軟なフレームワークです。
本記事では、環境構築からルーティング、テンプレート、フォームとバリデーション、データベース連携、テストとデバッグ、そして本番デプロイやセキュリティまで、一連の流れを概観しました。
まずはシンプルなアプリで手を動かしながら基本を押さえ、その後、拡張機能やクラウド・コンテナ技術を組み合わせて、より実践的なFlaskアプリケーション開発へとステップアップしていってください。
