FastAPIで独自のフォーラムを構築する:ステップ3 - HTMLテンプレート
James Reed
Infrastructure Engineer · Leapcell

前の記事では、フォーラムにPostgreSQLデータベースを導入し、サーバーが再起動してもデータが失われないように永続的なデータストレージを実現しました。
これにより、自信を持ってさらに改善を進めることができます。しかし、現在のインターフェーススタイル(HTML)がすべて main.py
に直接記述されていることに気づいたかもしれません。これは、将来新しい機能を追加するたびに、さらに多くのHTMLを main.py
に詰め込まなければならないことを意味するのでしょうか?
これは書くのが面倒なだけでなく、大量のHTML文字列がPythonコードに混在することになり、コードの可読性と保守性が低下します。
この問題を解決するために、この記事ではJinja2テンプレートエンジンを導入し、バックエンドロジック(Python)とフロントエンド表示(HTML)を分離し、プロジェクト構造をより明確で保守しやすくします。
ステップ1:Jinja2のインストール
FastAPIで公式に推奨されているテンプレートエンジンはJinja2です。仮想環境がアクティブになっていることを確認し、次のコマンドを実行してください:
pip install jinja2
ステップ2:templatesディレクトリとファイルの作成
プロジェクトをより整理するために、HTMLテンプレートファイルを保存するための専用ディレクトリを作成する必要があります。
プロジェクトのルートディレクトリで、templates
という名前の新しいフォルダーを作成します。
fastapi-forum/
├── templates/ <-- 新しいディレクトリ
│ └── posts.html <-- 新しいテンプレートファイル
├── main.py
├── database.py
├── models.py
└── venv/
次に、main.py
の generate_html_response
関数からHTMLコードを新しい templates/posts.html
ファイルに移動し、Jinja2構文を使用して変更します。
templates/posts.html
<!DOCTYPE html> <html> <head> <title>My FastAPI Forum</title> <style> body { font-family: sans-serif; margin: 2em; } input, textarea { width: 100%; padding: 8px; margin-bottom: 10px; box-sizing: border-box; } button { padding: 10px 15px; background-color: #007bff; color: white; border: none; cursor: pointer; } button:hover { background-color: #0056b3; } </style> </head> <body> <h1>Welcome to My Forum</h1> <h2>Create a New Post</h2> <form action="/api/posts" method="post"> <input type="text" name="title" placeholder="Post title" required /><br /> <textarea name="content" rows="4" placeholder="Post content" required></textarea><br /> <button type="submit">Post</button> </form> <hr /> <h2>Post list</h2> {% for post in posts %} <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;"> <h3>{{ post.title }} (ID: {{ post.id }})</h3> <p>{{ post.content }}</p> </div> {% endfor %} </body> </html>
主な変更点は Post list
セクションです:
- Pythonのforループを置き換えるために
{% for post in posts %}
と{% endfor %}
を使用します。 {{ post.title }}
のような二重中括弧構文を使用して、変数を動的に挿入します。
posts
変数は、テンプレートをレンダリングする際にFastAPIバックエンドによって渡されます。
ステップ3:FastAPIでのテンプレートの設定と使用
HTMLが分離されたので、main.py
を変更して、FastAPIがこれらのテンプレートファイルを見つけて使用する方法を指示する必要があります。
main.py
(最終版)
from fastapi import FastAPI, Form, Depends, Request from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, desc from typing import List import models from database import get_db app = FastAPI() # 1. Jinja2Templatesの設定 templates = Jinja2Templates(directory="templates") # --- Routes --- @app.get("/", response_class=RedirectResponse) def read_root(): return RedirectResponse(url="/posts", status_code=303) # ページの表示用ルート @app.get("/posts", response_class=HTMLResponse) async def view_posts(request: Request, db: AsyncSession = Depends(get_db)): # データベースからすべての投稿をクエリ result = await db.execute(select(models.Post).order_by(desc(models.Post.id))) posts = result.scalars().all() # 2. テンプレートを使用してレンダリング return templates.TemplateResponse("posts.html", {"request": request, "posts": posts}) @app.post("/api/posts") async def create_post( title: str = Form(...), content: str = Form(...), db: AsyncSession = Depends(get_db) ): # 新しいPostオブジェクトを作成 new_post = models.Post(title=title, content=content) # データベースセッションに追加 db.add(new_post) # コミットしてデータベースに保存 await db.commit() # オブジェクトをリフレッシュして新しく生成されたIDを取得 await db.refresh(new_post) return RedirectResponse(url="/posts", status_code=303)
どのようなコアな変更を行いましたか?
fastapi.templating
からJinja2Templates
をインポートしました。templates = Jinja2Templates(directory="templates")
でテンプレートエンジンインスタンスを作成し、テンプレートファイルがtemplates
ディレクトリに格納されていることを示しました。- HTML文字列を連結するために使用されていた以前の
generate_html_response
関数を削除し、HTMLResponse
を返す代わりに、templates.TemplateResponse()
を呼び出すようにしました。 TemplateResponse
は、テンプレートファイル名("posts.html"
)と、テンプレートに渡されるすべてのデータを含むコンテキスト辞書というパラメータを受け取ります。request
オブジェクトとデータベースからクエリされたposts
リストを渡しました。
ステップ4:実行して確認
uvicornサーバーを再起動します:
uvicorn main:app --reload
ブラウザを開き、http://127.0.0.1:8000
にアクセスします。ページの外観は以前とまったく同じで、すべての機能も正常に動作していることがわかります。
しかし、プロジェクトの内部構造は完全に異なります。Pythonコードはデータとロジックの処理のみを担当し、HTMLコードはコンテンツの表示に焦点を当てるようになりました。これにより、将来的にページスタイルを変更したり、新機能を追加したりすることが、はるかに簡単かつ効率的になります。
プロジェクトのオンラインデプロイ
最初のチュートリアルと同様に、このステップの結果をオンラインでデプロイして、プロジェクトの変更と進捗を友人と体験することができます。
簡単なデプロイソリューションは、Leapcell を使用することです。
以前にデプロイしたことがある場合は、コードをGitリポジトリにプッシュするだけで、Leapcellが最新コードを自動的に再デプロイします。
Leapcellのデプロイサービスを使用したことがない場合は、こちらの記事 のチュートリアルを参照してください。
まとめ
これで、FastAPIプロジェクトにJinja2テンプレートエンジンを正常に統合しました。
現在のフォーラムでは誰でも匿名で投稿できますが、これは実際にはフォーラムとは言えません。真のコミュニティは、異なるユーザーがいるだけでなく、各ユーザーが独自のアイデンティティを持っている必要があります。
次の記事では、フォーラムにユーザーシステムを追加し、ユーザー登録とログイン機能を実装して、各ユーザーが独自のアイデンティティでフォーラムにアクセスできるようにします。