Flask を使った Todo リスト&LINE Bot 開発ハンズオン
目次
- 目次
- はじめに
- 前提条件と準備
- 1. Flask プロジェクト構成の作成
- 2. SQLite データベースの準備
- 3. Todo リスト API の実装
- 5. LINE Messaging API のセットアップ
- 6. Flask で LINE Webhook を処理して Todo 操作
- まとめ:Flask アプリ構造のポイント
はじめに
エンジニアが Flask アプリの構造を学ぶためのハンズオンとして、SQLite を用いた簡易 Todo リスト API と、LINE 公式 SDK(line-bot-sdk-python
)を使った Messaging API 連携 Bot を構築します。Next.js/TypeScript が主なバックグラウンドで、Python はスクリプト程度の経験という想定読者向けに、フロントエンド(Jinja テンプレートや Next.js)は使用せず、LINE 上のメッセージ(Webhook)経由で操作する形に絞っています。ハンズオンを通じて Flask のプロジェクト構造(アプリケーションファクトリ、Blueprint、サービス層の分離など)を体験し、最終的に以下の成果物を得ます:
- Todo リスト API(SQLite 永続化) – Todo アイテムの追加・一覧・削除を行う REST API(GET, POST, DELETE)
- LINE ボット – 「ADD」「DELETE」「LIST」のテキストコマンドで Todo を操作できる LINE Messaging API 対応のボット
- Flask アプリの構成理解 – Blueprint によるモジュール分割やアプリケーションファクトリパターンを採用したプロジェクト構造
所要時間は約 3 時間を想定しています。各ステップでコード例や実行例を示し、さらに発展的な内容は「オプション」として後述します。
前提条件と準備
-
前提環境:MacOS 上で Python 3 系がインストール済みであること(ターミナルで
python3 --version
コマンドで確認できます)。また、LINE アカウントを持ち、LINE Developers での開発者登録が済んでいることを前提とします(Messaging API 用のチャネル作成に必要)。SQLite は Python に標準搭載されています。
-
ディレクトリ作成
作業用のプロジェクトディレクトリを作成しましょう。例としてターミナルで以下を実行します:
$ mkdir flask_todo_line_bot && cd flask_todo_line_bot
-
Python 仮想環境の構築
Python の仮想環境ツール venv を使い、本プロジェクト用の独立した環境を用意します。次のコマンドでプロジェクトフォルダ内に仮想環境を作成・有効化します:$ python3 -m venv venv # 仮想環境フォルダを作成(名前は venv としています) $ source venv/bin/activate # 仮想環境を有効化する
成功すると、ターミナルの先頭に (venv) と表示され、仮想環境下でコマンド実行されるようになります。以降はこの環境で作業してください。
-
必要パッケージのインストール
仮想環境が有効になった状態で、Flask と LINE SDK をインストールします。(venv) $ python -m pip install --upgrade pip (venv) $ pip install Flask line-bot-sdk python-dotenv
1. Flask プロジェクト構成の作成
Flask ではシンプルなスクリプト 1 ファイルでアプリを作ることも可能ですが、規模が大きくなるほどディレクトリを分割し Blueprint を用いることでコードを整理するメリットが大きいです。ここでは MVC 的な役割分担を意識しつつ、以下の構成を用いてアプリケーションファクトリ+Blueprint を採用します。
Blueprint とは:Flask の Blueprint(ブループリント)は、アプリケーションを機能ごとに分割するための仕組みです。関連するビュー(ルート)、テンプレート、静的ファイルなどをグループ化し、モジュール化された再利用可能なコンポーネントとして定義できます。例えば、ユーザー認証、管理画面、API など、機能ごとに別々の Blueprint として実装することで、コードの管理が容易になります。Blueprint 自体は直接実行できず、Flask アプリケーションに登録して初めて機能します。
アプリケーションファクトリとは:Flask のアプリケーションファクトリとは、Flask アプリケーションオブジェクト(
Flask(__name__)
で作成されるもの)を返す関数のことです。この関数内でアプリの設定、拡張機能の初期化、Blueprint の登録などを行います。このパターンを使うことで、テスト時に異なる設定を適用したり、複数のインスタンスを作成したりと、柔軟にアプリケーションを構成できます。Flask の公式ドキュメントでも推奨されているアプローチで、特に大規模なアプリケーション開発に適しています。
flask_todo_line_bot/
├── app/
│ ├── __init__.py ← Flask アプリ生成(ファクトリ)と Blueprint 登録
│ ├── models.py ← Todo モデル定義と DB 操作(データ層)
│ ├── todo_service.py ← Todo のビジネスロジック(サービス層)
│ └── routes/ ← ルーティング用 Blueprint モジュール群(コントローラ層)
│ ├── todo_routes.py ← Todo CRUD 用の API ルート定義
│ └── line_routes.py ← LINE Webhook 用のルート定義
├── run.py ← アプリケーション起動スクリプト
└── requirements.txt ← (必要に応じて)依存パッケージ一覧
- app/init.py: Flask のアプリケーションファクトリ create_app()関数を定義し、アプリ設定や Blueprint 登録を行う。
- app/models.py/app/todo_service.py: Todo モデルやビジネスロジックの実装。今回は SQLite アクセスを直接書きます。
- app/routes/: Flask の Blueprint を使ってビュー関数(ルーティング)を機能ごとにまとめる。
- run.py: アプリを起動するスクリプト。
上記の構成図を再現します。
2. SQLite データベースの準備
Todo を永続化するため、組み込みデータベースの SQLite を使用します。Flask に標準 ORM はないため、ここでは Python の sqlite3 モジュールで直接 SQL を実行します。必要になれば SQLAlchemy などを使いましょう。
app/init.py
まず、Flask アプリ生成用のファクトリ関数を定義し、DB ファイル名や初期化処理を設定します。
# app/__init__.py
from flask import Flask
from dotenv import load_dotenv
import os
# .env ファイルから環境変数を読み込む
load_dotenv()
def create_app():
"""Flask アプリ生成用ファクトリ関数"""
app = Flask(__name__)
app.config['DB_PATH'] = 'todos.db' # SQLite データベースファイルのパス
# データベース初期化
with app.app_context():
from app import todo_service
todo_service.init_db()
# このあとでBlueprintを登録
from app.routes import todo_routes, line_routes
app.register_blueprint(todo_routes.bp)
app.register_blueprint(line_routes.bp)
return app
app/todo_service.py
DB 初期化関数と、Todo の CRUD 操作を行う関数を実装します。init_db()でテーブルが無ければ作成し、それ以外は Todo の追加・一覧・削除を担当。
# app/todo_service.py
import sqlite3
from flask import current_app
def init_db():
"""データベースの初期化(テーブルがなければ作成)"""
db_path = current_app.config['DB_PATH']
conn = sqlite3.connect(db_path)
conn.execute(
"CREATE TABLE IF NOT EXISTS todos ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"content TEXT NOT NULL)"
)
conn.commit()
conn.close()
def create_todo(content):
"""新しい Todo を追加し、作成されたレコードを返す"""
conn = sqlite3.connect(current_app.config['DB_PATH'])
cur = conn.cursor()
cur.execute("INSERT INTO todos (content) VALUES (?)", (content,))
conn.commit()
new_id = cur.lastrowid
conn.close()
return {"id": new_id, "content": content}
def get_all_todos():
"""Todo を全件取得してリストで返す"""
conn = sqlite3.connect(current_app.config['DB_PATH'])
cur = conn.cursor()
cur.execute("SELECT id, content FROM todos")
rows = cur.fetchall()
conn.close()
return [{"id": r[0], "content": r[1]} for r in rows]
def delete_todo(todo_id):
"""指定 ID の Todo を削除する。削除できたら True を返す"""
conn = sqlite3.connect(current_app.config['DB_PATH'])
cur = conn.cursor()
cur.execute("DELETE FROM todos WHERE id = ?", (todo_id,))
conn.commit()
deleted = (cur.rowcount > 0)
conn.close()
return deleted
3. Todo リスト API の実装
app/routes/todo_routes.py
Todo CRUD を実行できる API を Flask の Blueprint で定義します。/todos/配下の URL で操作し、JSON を返す形です。
# app/routes/todo_routes.py
from flask import Blueprint, request, jsonify, abort
from app import todo_service
bp = Blueprint('todo_routes', __name__, url_prefix='/todos')
@bp.route('/', methods=['GET'])
def list_todos():
"""全 Todo を一覧取得"""
todos = todo_service.get_all_todos()
return jsonify(todos), 200
@bp.route('/', methods=['POST'])
def create_todo_item():
"""新規 Todo を追加"""
data = request.get_json()
if not data or 'content' not in data:
abort(400, description="Request JSON must include 'content'")
new_todo = todo_service.create_todo(data['content'])
return jsonify(new_todo), 201
@bp.route('/<int:todo_id>', methods=['DELETE'])
def delete_todo_item(todo_id):
"""指定 ID の Todo を削除"""
deleted = todo_service.delete_todo(todo_id)
if not deleted:
abort(404, description=f"Todo id={todo_id} not found")
return '', 204
4. アプリの起動と API 動作確認
run.py
開発用サーバの起動スクリプトを作成します。
# run.py
from app import create_app, todo_service
from dotenv import load_dotenv
# .env ファイルから環境変数を読み込む
load_dotenv()
app = create_app()
app.todo_service = todo_service # LINE ハンドラから使うために登録
if __name__ == '__main__':
app.run(debug=True)
ターミナルで実行し、curl などで API を呼び出して動作確認します。
# アプリ起動
(venv) $ python run.py
API 動作確認手順(curl コマンド例)
別のターミナルウィンドウを開き、以下の curl コマンドでテストしてみましょう:
# Todo 一覧取得 (GET)
$ curl http://localhost:5000/todos/
-
Todo 一覧 GET /todos/:初回は空配列
[]
が返る。
# Todo 追加 (POST)
$ curl -X POST -H "Content-Type: application/json" -d '{"content":"Buy milk"}' http://localhost:5000/todos/
-
Todo 追加 POST /todos/:
{"content":"Buy milk"}
を送信 →{"id":1,"content":"Buy milk"}
が返り、201 ステータス。
# Todo 削除 (DELETE)
$ curl -X DELETE http://localhost:5000/todos/1
- Todo 削除 DELETE /todos/1:削除成功で 204 が返る。
Postman などの API クライアントツールを使う場合も、同じエンドポイントとメソッドでリクエストを送信してください。
5. LINE Messaging API のセットアップ
LINE Bot を作成するには、LINE Developers でのチャネル作成と、Webhook URL の設定が必要です。以下の手順で進めましょう:
5.1 LINE Developers でのチャネル設定
- LINE Developers コンソールにログインする
- 新規プロバイダーを作成(初めての場合)または既存のプロバイダーを選択
- 「新規チャネル作成」から「Messaging API」を選択
- チャネル情報を入力:
- チャネル名:「Flask Todo Bot」など、ユーザーに表示される名前
- チャネル説明:簡単な説明
- 大業種・小業種:適切なものを選択
- メールアドレス:連絡先
- 利用規約に同意して「作成」ボタンをクリック
5.2 アクセストークンとシークレットの取得
- 作成したチャネルの「基本設定」タブを開く
- 「チャネルシークレット」をメモする(Python コードで使用)
- 「Messaging API 設定」タブを開く
- 「チャネルアクセストークン」の「発行」ボタンをクリック
- 発行されたトークンをメモする(Python コードで使用)
5.3 ngrok でローカルサーバーを公開
LINE の Webhook は HTTPS の URL が必要です。開発中は ngrok というツールを使って、ローカルの Flask サーバーを一時的にインターネットに公開します。
ngrok のインストールと使用方法
- ngrok の公式サイトでアカウント作成・ダウンロード
- ダウンロードした ngrok を適切な場所に解凍
- ターミナルで以下のコマンドを実行(Flask は 5000 番ポートで実行中と仮定)
# ngrok を実行
$ ./ngrok http 5000
- 実行すると以下のような画面が表示され、
https://xxxx.ngrok.io
という URL が生成されます
ngrok by @inconshreveable (Ctrl+C to quit)
Session Status online
Session Expires 1 hour, 59 minutes
Version 2.3.40
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding http://xxxx.ngrok.io -> http://localhost:5000
Forwarding https://xxxx.ngrok.io -> http://localhost:5000
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
- 表示された
https://xxxx.ngrok.io
の URL をメモします(xxxx は実行するたびに変わります)
5.4 Webhook URL の設定
- LINE Developers コンソールの「Messaging API 設定」タブに戻る
- 「Webhook 設定」セクションで以下の設定を行う:
- Webhook URL:
https://xxxx.ngrok.io/line/webhook
と入力(xxxx は ngrok で生成された値) - 「Webhook の利用」を「有効」に設定
- 「検証」ボタンをクリックし、「成功」と表示されることを確認
(Flask アプリが起動していないと失敗します)
- Webhook URL:
5.5 応答設定
- 「応答メッセージ」を「無効」に設定
- 「Webhook」のみを「有効」に設定
これで LINE Bot からのメッセージは全て Flask アプリで処理されるようになります。
5.6 Bot を友だち追加
- 「Messaging API 設定」タブ内の「QR コード」をスマホで読み取る
- または「Bot リンク」をスマホで開く
- 「友だち追加」ボタンをタップして Bot を友だちに追加
これで LINE Bot のセットアップは完了です。次に Flask アプリに LINE Webhook 処理コードを実装しましょう。
5.7 環境変数の設定(.env ファイル)
セキュリティのため、LINE のチャネルシークレットやアクセストークンはコードに直接書かず、環境変数として管理するのがベストプラクティスです。Flask で環境変数を扱うために、python-dotenv ライブラリを使用します:
python-dotenv のインストール
# 仮想環境内で実行
$ pip install python-dotenv
.env ファイルの作成
プロジェクトのルートディレクトリに .env
ファイルを作成し、以下の内容を記述します(値は自分のものに置き換えてください):
LINE_CHANNEL_SECRET=ここにチャネルシークレットを入力
LINE_CHANNEL_ACCESS_TOKEN=ここにチャネルアクセストークンを入力
セキュリティに関する注意
.env
ファイルには機密情報が含まれるため、Git リポジトリにコミットしないよう .gitignore
ファイルに追加することをお勧めします:
# .gitignore ファイルを作成し、.env を無視するよう設定
$ echo ".env" >> .gitignore
これで環境変数の設定は完了です。次に LINE Webhook を処理する Flask ルートを実装します。
6. Flask で LINE Webhook を処理して Todo 操作
app/routes/line_routes.py
公式 SDK(line-bot-sdk-python)を使うことで、署名検証・返信 API の呼び出しをシンプルに記述できます。
# app/routes/line_routes.py
import os
from flask import Blueprint, request, abort, current_app
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage
from dotenv import load_dotenv
# .env ファイルから環境変数を読み込む
load_dotenv()
# 環境変数から LINE の設定を取得
CHANNEL_SECRET = os.getenv("LINE_CHANNEL_SECRET")
CHANNEL_ACCESS_TOKEN = os.getenv("LINE_CHANNEL_ACCESS_TOKEN")
# 環境変数が設定されているか確認(開発時の助けになります)
if not CHANNEL_SECRET or not CHANNEL_ACCESS_TOKEN:
print("Warning: LINE API の認証情報が設定されていません。.env ファイルを確認してください。")
line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(CHANNEL_SECRET)
bp = Blueprint('line_routes', __name__, url_prefix='/line')
@bp.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Line-Signature')
if signature is None:
abort(400, description="Missing signature")
body = request.get_data(as_text=True)
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400, description="Invalid signature")
return 'OK'
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
user_message = event.message.text.strip()
reply_token = event.reply_token
service = current_app.todo_service
# コマンド判定
if user_message.lower().startswith("add "):
content = user_message[4:].strip()
if content:
new_todo = service.create_todo(content)
reply_text = f"Todo追加: (id={new_todo['id']}) {new_todo['content']}"
else:
reply_text = "追加するTodoの内容を入力してください。"
elif user_message.lower().startswith("delete "):
parts = user_message.split(maxsplit=1)
if len(parts) < 2 or not parts[1].isdigit():
reply_text = "削除コマンドの形式: DELETE <数字ID>"
else:
todo_id = int(parts[1])
deleted = service.delete_todo(todo_id)
if deleted:
reply_text = f"Todo id={todo_id} を削除しました。"
else:
reply_text = f"Todo id={todo_id} が見つかりません。"
elif user_message.lower() == "list":
todos = service.get_all_todos()
if not todos:
reply_text = "登録されているTodoはありません。"
else:
lines = ["[Todo一覧]"]
for t in todos:
lines.append(f"- {t['id']}: {t['content']}")
reply_text = "\n".join(lines)
else:
reply_text = (
"コマンドを入力してください:\n"
"・Todo追加: ADD <内容>\n"
"・Todo削除: DELETE <ID>\n"
"・Todo一覧: LIST"
)
line_bot_api.reply_message(reply_token, TextSendMessage(text=reply_text))
ポイント:
- @bp.route('/webhook', methods=['POST']) で Webhook URL を受け取り、LINE SDK の handler.handle を呼ぶだけで署名検証・イベントパースを実行。
- イベントハンドラ @handler.add(...) で TextMessage が届いたときの処理を定義。ユーザーからのメッセージに応じて Todo を操作し、line_bot_api.reply_message で返信メッセージを返す。
- current_app.todo_service により Todo 操作関数(create, delete, list など)を呼び出し。
動作確認
Flask を再起動し、ngrok を立ち上げ、LINE から Bot に「ADD <内容>」「DELETE <ID>」「LIST」などを送信してみましょう。正常に Webhook が飛び、Todo 操作が行われ、返信が届けば成功です。
まとめ:Flask アプリ構造のポイント
-
アプリケーションファクトリ (create_app)
- Flask アプリを生成・設定する公式推奨パターン。テストやデプロイ時にも柔軟。
-
Blueprint によるモジュール分割
- Todo 管理機能、LINE 連携機能をそれぞれ todo_routes・line_routes に分けることで可読性 UP。
-
サービス層の分離
- ルート関数から直接 SQL を書くのではなく、todo_service を挟むことでビジネスロジックをまとめる。
-
LINE 公式 SDK
- 署名検証や返信 API 呼び出しを簡潔に書ける。イベントハンドラをデコレータで管理できる。
オプション: 発展的な改善・拡張案
- 完了フラグ/DONE コマンドの導入: テーブルに done カラムを追加し、完了・未完了を切り替える。
- UPDATE(EDIT)コマンド: 既存 Todo 内容を更新する機能。
- テストコードの追加: pytest などを用いてユニットテスト/Flask テストクライアントで API テストを導入。
- ORM の利用: SQLAlchemy を導入し、モデル定義をよりリッチに。
- デプロイ: Heroku や Railway などの PaaS にデプロイし、環境変数で LINE シークレットを安全に管理。
これで Flask × SQLite × LINE SDK の基本的な Todo アプリが完成です。ぜひ本稿をベースに自分の Bot 機能を拡張してみてください。
Discussion