【Flask】メモ帳アプリを作ろう|Chapter7-3

一つ前のページではBlueprintについて学習しました。
今回は メモ帳アプリ を作成していきましょう。
Chapter1:Flask入門編
Chapter2:Jinja2入門編
Chapter3:フィルター編
Chapter4:フォーム編
Chapter5:データベース編
Chapter6:エラーハンドリングとデバッグ編
Chapter7:アプリ開発編
・Chapter7-1:おみくじアプリを作ろう
・Chapter7-2:Blueprintを使おう
・Chapter7-3:メモ帳アプリを作ろう ◁今回はここ
・Chapter7-4:ToDoアプリを作ろう
Flaskの学習の集大成として、実際に使えるWebアプリケーション「メモ帳アプリ」を作成しましょう。
このアプリはユーザーがメモを追加し、その一覧を確認し、不要になったメモを削除することができる、いわゆる CRUD(Create, Read, Delete)操作 を体験できる内容です。
また、このアプリではBlueprintというFlaskの機能を使ってアプリケーションをモジュールごとに分割しています。
これにより、複数の機能を持つ大規模なアプリケーションでも、コードを整理しやすくなります。
他にも、データベース、フォーム処理(WTForms)、ルーティング、テンプレートエンジン(Jinja2)など、これまでに学んできたFlaskの機能をすべて活用します。
この章で学ぶ内容をまとめると以下の通りです:
- アプリの構成とディレクトリ構造
- モデルクラスとデータベース操作
- フォームクラスの作成とバリデーション
- ルーティングの定義と画面遷移処理
- テンプレートエンジンを使った画面表示
それでは、アプリケーション全体の構成から見ていきましょう。
- Flask開発を Stream Deck でボタン化しよう!
-
Flaskは非常に軽量かつシンプルなフレームワークですが、それゆえに定型作業が多く、開発は単調な作業の連続になりがちです。
それこそがFlaskのメリットであり、習得難易度が低い理由でもありますが、単調な作業は退屈で、ミスも起こりやすいでしょう。
そこで役に立つのが Stream Deck 。
このような定型手順が多い作業を “ボタン化” することで視覚化。
圧倒的に 効率的 かつ ストレスフリー な開発環境が簡単に手に入ります↓↓
あわせて読みたいFlask開発をStreamDeckでボタン化しようあわせて読みたいプログラマー向けStream Deckの選び方|初心者でも失敗しないモデル比較ガイド
Flaskメモ帳アプリの概要と全体構成

今回作成する「メモ帳アプリ」は、簡単にメモを記録し、一覧で確認し、不要なメモを削除できるシンプルなアプリケーションです。
このアプリでは、以下の機能を実装します:
- 新しいメモを追加(フォームを使用)
- メモの一覧を表示(データベースから取得)
- メモを削除(指定IDのデータを削除)
このような機能を整理して実装するために、FlaskのBlueprint機能を活用して、アプリケーションを機能ごとに分割しています。
以下がディレクトリとファイルの構成です。実際にコードを書き移す前に、フォルダだけ先に作っておくと失敗しにくいのでお勧めです。
memo_app/ ├── app/ │ ├── __init__.py ← Flaskアプリケーションの初期化処理 │ ├── models.py ← データベースのモデルクラスを定義 │ ├── forms.py ← WTFormsで使うフォームを定義 │ └── memo/ ← メモ機能のルーティングとテンプレート │ ├── __init__.py ← Blueprintオブジェクトの作成と登録 │ ├── routes.py ← ルーティングの定義(一覧・追加・削除) │ └── templates/ │ └── memo/ │ ├── list.html ← メモ一覧を表示するテンプレート │ ├── form.html ← メモを追加するフォームテンプレート │ └── base.html ← 基本となるHTMLレイアウト ├── config.py ← 設定ファイル(DB設定など) └── run.py ← アプリケーション起動用のエントリーポイント
このように各機能を分担した形でファイルが構成されています。
特にmemo/ディレクトリ以下はBlueprintでまとめられており、「メモ機能」だけを独立して管理することができます。
アプリ起動ファイルと設定ファイル
このセクションでは、Flaskアプリケーションを構成する上で欠かせない2つのファイル、config.pyとrun.pyについて解説します。
この2つのファイルはmemo_appフォルダ直下に保存してください。
Flaskアプリの設定を集約するファイル|config.py
このファイルでは、Flaskアプリに関する設定値を一箇所にまとめています。
アプリケーションの挙動を変更したいとき、このファイルを編集すれば一括して制御できます。
以下がそのコードです:
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
# セキュリティ用の秘密鍵(フォームのCSRF保護などに使用)
SECRET_KEY = os.environ.get('SECRET_KEY', 'dev_secret_key')
# データベースの接続URI(環境変数がなければローカルのSQLiteファイルを使う)
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI') \
or 'sqlite:///' + os.path.join(basedir, 'memo_app.db')
# SQLAlchemyの通知機能を無効化(警告回避のため)
SQLALCHEMY_TRACK_MODIFICATIONS = FalseこのクラスConfigは、Flaskアプリに設定を渡すためのコンテナのような役割を果たします。特に重要なのは以下の3点です:
SECRET_KEY: フォームの保護に必要な設定。開発中は固定値、公開時は環境変数で安全に管理。SQLALCHEMY_DATABASE_URI: データベースとの接続先。ここではSQLiteを使用。SQLALCHEMY_TRACK_MODIFICATIONS: 無効にすることでパフォーマンス向上。
このファイルに設定をまとめることで、複数環境(開発・本番など)での切り替えが容易になります。

アプリケーションのエントリーポイント|run.py
このファイルはアプリケーションを実行するためのスタート地点です。
以下がコードです:
from app import create_app
# Flaskアプリケーションの生成
app = create_app()
# アプリケーションの起動
if __name__ == "__main__":
app.run(debug=True)ここでの重要なポイントはcreate_app()という関数を通じてアプリケーションを生成している点です。
この関数は、このあとapp/__init__.pyの中で定義します。Blueprintの登録や設定の読み込みなどを行うものです。
この構造は「アプリケーションファクトリパターン」と呼ばれ、大規模なFlaskアプリでよく使われます。
直接Flask(__name__)で生成するのではなく、関数で包むことで柔軟に拡張が可能になります。
app.run(debug=True)は 開発モード(デバッグモード)で起動する設定であり、エラー時に詳細なデバッグ情報が表示される利点があります。

アプリ初期化とモデル・フォーム定義の基本
このセクションではアプリケーションの初期化処理、ユーザー入力フォームの定義、データベースモデルの構築について解説します。
これらはFlaskアプリにおける「設定」「入力」「保存」の基盤となる部分です。
以下の3つのコードをmemo_appフォルダの中のappフォルダ直下に保存してください。
アプリケーションの初期化とBlueprint登録|__init__.py
このファイルはFlaskアプリケーションを生成する関数create_appを定義しています。
Blueprintの登録やデータベースの初期化もここで行います。
from flask import Flask, redirect
from flask_sqlalchemy import SQLAlchemy
from config import Config
db = SQLAlchemy() # SQLAlchemyのインスタンス作成
def create_app():
app = Flask(__name__, template_folder='templates')
app.config.from_object(Config) # 設定の読み込み
db.init_app(app) # アプリにDBを紐付ける
with app.app_context(): # アプリのコンテキストで初期化処理
from app.memo import bp as memo_bp
app.register_blueprint(memo_bp, url_prefix='/memo') # Blueprintの登録
db.create_all() # モデルに基づいてテーブルを作成
@app.route('/')
def root():
return redirect('/memo/') # トップアクセスはメモ一覧にリダイレクト
return app主な処理の役割:
Flask(__name__):Flaskアプリを生成app.config.from_object(Config):設定ファイルから設定を読み込むdb.init_app(app):データベース機能をFlaskアプリに統合app.register_blueprint(...):Blueprintによってルーティングなどを外部モジュール化db.create_all():モデル定義に基づいてテーブルを生成
WTFormsを用いたフォームの定義|forms.py
続いてメモ入力用のフォームを定義します。
Flask-WTF(WTFormsの拡張)を使うことで、HTMLフォームをPythonコードで安全かつ簡単に記述できます。
from flask_wtf import FlaskForm # FlaskでWTFormsを使うための基底クラス
from wtforms import StringField, SubmitField # テキスト入力欄と送信ボタンのフィールドをインポート
from wtforms.validators import DataRequired, Length # 入力検証(バリデーション)用の関数
# MemoFormクラスは、メモの追加や編集に使用するフォームを定義する
class MemoForm(FlaskForm):
# contentフィールド:メモ本文を入力するテキストボックス
# ラベルは「内容」、空欄禁止(DataRequired)、最大200文字(Length)
content = StringField('内容', validators=[DataRequired(), Length(max=200)])
# submitフィールド:送信ボタン。ラベルは「送信」
submit = SubmitField('送信')このフォームには以下のフィールドがあります:
content:メモ本文を入力するフィールド(最大200文字、必須)submit:送信ボタン
DataRequired()バリデータにより、空欄での送信が拒否され、Length(max=200)で文字数制限をしています。

データベースモデルの定義|models.py
続いてデータベースに保存するデータ構造をクラスで定義します。
ORM(Object Relational Mapper)であるSQLAlchemyを使って、Pythonコードでデータベースのテーブルを操作します。
from app import db # SQLAlchemyのインスタンスをインポート
from datetime import datetime # 現在日時を扱うための標準ライブラリ
# Memoクラスは、データベースの'memo'テーブルに対応するモデルクラス
class Memo(db.Model):
# idカラム:各メモを一意に識別するための主キー(自動的に連番で振られる)
id = db.Column(db.Integer, primary_key=True)
# contentカラム:メモの本文を保存するフィールド(最大200文字、空欄禁止)
content = db.Column(db.String(200), nullable=False)
# timestampカラム:メモの作成時刻を保存。デフォルトで現在の日時を自動設定
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
# __repr__メソッド:デバッグ時などにインスタンスを分かりやすく表示するための文字列表現
def __repr__(self):
return f'<Memo {self.id}>'各フィールドの説明:
id:主キー。自動的に一意な値が割り当てられます。content:メモ本文。空欄不可。最大200文字。timestamp:メモ作成時刻。デフォルトで現在時刻が設定されます。
このモデルに基づき、Flaskは自動でSQLiteに対応するテーブルを生成します。

BlueprintによるルーティングとCRUD機能の実装
このセクションでは、メモ帳アプリにおける主要な機能「一覧表示、追加、編集、削除」を、どのようにルーティングして処理しているかを詳しく解説します。
以下の2つのコードを memo_app/app/memo に保存してください。

Blueprintオブジェクトの作成と登録|__init__.py
from flask import Blueprint
# Blueprintオブジェクトを生成。名前は 'memo'、テンプレートの場所を指定
bp = Blueprint('memo', __name__, template_folder='templates')
# ルーティング定義を読み込むことで、このBlueprintにルートを紐づける
from app.memo import routesこのコードは memo という名前のBlueprintを定義しています。
BlueprintとはFlaskアプリケーションを構造的に分割するための仕組みです。これにより、「メモ機能」だけをこのディレクトリ内に独立して記述できます。
ルーティングの定義(一覧・追加・削除)|routes.py
# memo/routes.py
from flask import render_template, redirect, url_for, flash, request, abort
from app.memo import bp # Blueprintインスタンス
from app import db # データベース操作用のSQLAlchemyインスタンス
from app.models import Memo # データベースのモデルクラス
from app.forms import MemoForm # WTFormsによる入力フォーム
@bp.route('/')
def index():
# メモを作成日時の降順で取得して一覧表示
memos = Memo.query.order_by(Memo.timestamp.desc()).all()
return render_template('memo/list.html', memos=memos)
@bp.route('/add', methods=['GET', 'POST'])
def add():
form = MemoForm() # フォームオブジェクトの作成
if form.validate_on_submit(): # POSTかつバリデーション通過時の処理
memo = Memo(content=form.content.data) # フォームからメモを生成
db.session.add(memo) # データベースに追加
db.session.commit() # 変更を保存
flash('メモを追加しました。') # メッセージ表示
return redirect(url_for('memo.index')) # 一覧ページへ移動
return render_template('memo/form.html', form=form, title='メモ追加') # フォーム表示
@bp.route('/<int:id>/edit', methods=['GET', 'POST'])
def edit(id):
memo = Memo.query.get_or_404(id) # メモをIDで取得。なければ404
form = MemoForm(obj=memo) # フォームに既存データを渡す
if form.validate_on_submit():
memo.content = form.content.data # データを更新
db.session.commit()
flash('メモを更新しました。')
return redirect(url_for('memo.index'))
return render_template('memo/form.html', form=form, title='メモ編集')
@bp.route('/<int:id>/delete', methods=['POST'])
def delete(id):
memo = Memo.query.get_or_404(id) # メモを取得。なければ404
db.session.delete(memo) # メモを削除
db.session.commit() # 変更を保存
flash('メモを削除しました。')
return redirect(url_for('memo.index'))このルーティングファイルでは、/memo/ 以下のエンドポイントに対する4つの処理を定義しています:
- 一覧表示(
/memo/)- データベースから全メモを取得し、作成日時の降順にソートして
list.htmlに渡しています。 - Jinjaテンプレートでループ表示される仕組みです。
- データベースから全メモを取得し、作成日時の降順にソートして
- 追加(
/memo/add)GETリクエストでは空のフォームを表示し、POSTリクエストでは送信された内容をバリデーションし、データベースに保存します。- 成功時にはメッセージを表示して一覧ページへリダイレクトします。
- 編集(
/memo/<id>/edit)- 既存メモを取得し、フォームに初期値として渡します。
- 編集後にバリデーションを通過すれば、内容を更新して保存します。
- 削除(
/memo/<id>/delete)- 対象のメモを取得し、削除します。
- このルートはPOSTのみ対応としており、安全性に配慮しています。
これらのルートはすべて、Flaskの典型的な処理パターンに従っており、初心者でも理解しやすく、実際のWebアプリ開発にもそのまま応用できる構造です。

HTMLテンプレート構成とデザインの工夫
このセクションでは、Flaskにおけるテンプレートエンジン「Jinja2」を用いて構築されたHTMLファイルを解説します。
Flaskアプリでは、HTMLにPythonのデータや制御構文を埋め込むことで、動的なWebページを簡単に作ることができます。
以下の3つのコードを memo_app/app/memo/templates/memo に保存してください。

基本となるHTMLレイアウト|base.html
このファイルはすべてのページの「土台」となるテンプレートで、ナビゲーションリンクやメッセージ表示の機能を共通化しています。
{% block %}を使うことで、個別のページで必要な部分だけ上書きできます。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>メモ帳アプリ{% block title %}{% endblock %}</title>
</head>
<body>
{# Flashメッセージ(成功・エラー通知)の表示ブロック #}
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for m in messages %}
<li>{{ m }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{# ナビゲーションリンク #}
<a href="{{ url_for('memo.index') }}">一覧</a> |
<a href="{{ url_for('memo.add') }}">新規追加</a>
<hr>
{# 子テンプレートにコンテンツを差し込ませるためのブロック #}
{% block content %}{% endblock %}
</body>
</html>メモ一覧を表示するテンプレート|list.html
このテンプレートでは、渡されたmemosリストをfor文でループし、それぞれのメモを表示します。
メモごとに編集リンクと削除ボタンがあり、削除処理はセキュリティ上POSTメソッドで行われます。
また、メモが1つも存在しない場合には、{% else %}ブロックが実行され、「メモがありません。」と表示されます。
{% extends 'memo/base.html' %}
{% block title %} - 一覧{% endblock %}
{% block content %}
<h1>メモ一覧</h1>
<ul>
{% for memo in memos %}
<li>
{{ memo.timestamp.strftime('%Y-%m-%d %H:%M') }} - {{ memo.content }}
[<a href="{{ url_for('memo.edit', id=memo.id) }}">編集</a>]
<form action="{{ url_for('memo.delete', id=memo.id) }}" method="post" style="display:inline">
<button type="submit">削除</button>
</form>
</li>
{% else %}
<li>メモがありません。</li>
{% endfor %}
</ul>
{% endblock %}メモを追加するフォームテンプレート|form.html
このテンプレートはメモの追加・編集のどちらにも共通して使われます。
title変数に応じてタイトルが変化し、フォームの内容も動的に変わります。
バリデーションエラーがある場合には、エラーメッセージが赤文字で表示されます。
{% extends 'memo/base.html' %}
{% block title %} - {{ title }}{% endblock %}
{% block content %}
<h1>{{ title }}</h1>
<form method="post">
{{ form.hidden_tag() }} {# CSRFトークンなどを自動挿入する隠しフィールド #}
<p>
{{ form.content.label }}<br> {# フィールドのラベル #}
{{ form.content(size=40) }} {# テキスト入力欄 #}
{% for err in form.content.errors %}
<span style="color:red">{{ err }}</span>
{% endfor %}
</p>
<p>{{ form.submit() }}</p> {# 送信ボタン #}
</form>
{% endblock %}これらのテンプレートはすべてbase.htmlを土台としており、コードの再利用性が高く、修正がしやすい構成になっています。
FlaskとJinja2の力を活用することで、柔軟で整ったWebページが実現されています。
- サイト改善アンケート|1分だけ、ご意見をお聞かせください
-
本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、ご利用者のみなさまの「プログラミングを学習する理由」などをアンケート形式でお伺いしています。ご協力いただけますと幸いです。
アンケート
まとめ|Flaskでメモアプリを構築する流れを振り返る
この章では、Flaskを用いた本格的なWebアプリケーションの開発として、「メモ帳アプリ」を完成させました。
今回の学びを振り返ると、以下のような要素がありました。
- Flaskの Blueprint 機能を使って、機能ごとにアプリを整理・分割する方法を学びました。
- SQLAlchemy を用いて、データベースと連携したモデルクラスを定義し、データの保存・取得・削除を行う方法を習得しました。
- WTForms でフォームを作成し、バリデーションやユーザー入力処理を実装しました。
render_template()、redirect()、url_for()、flash()など、Flaskでよく使われる関数を実際に使って動的なWebページを作りました。- Jinja2テンプレートエンジンを用いて、HTML内でデータを繰り返し表示したり、条件によって出力を切り替える手法を学びました。
このように、Flaskの機能をフル活用した「ミニアプリケーション」の制作を通して、Webアプリ開発の一連の流れを経験できたことは大きな収穫です。
小さなアプリでも、自分の手でゼロから構築し、動くものを作り上げるという経験は、大きな自信に繋がります。
今後より複雑なアプリケーションを作っていくための土台として、今回の内容をしっかりと復習し、自分の中に定着させましょう。
次回のChapterでは、さらに発展的な内容として「ToDoアプリ」の開発に取り組みます。
ユーザーごとのタスク管理やログイン機能など、さらに実践的なスキルが身につく内容です。
この調子で、一歩ずつスキルを積み重ねていきましょう!






