FastAPIで完璧なブログを構築:タグでフィルタリング
Wenhao Wang
Dev Intern · Leapcell

前の記事では、ブログにタグを作成して表示する機能を追加しました。
次に、タグ機能の残りの部分、つまりタグで投稿をフィルタリングする機能を完成させます。
ユーザーがタグリンクをクリックすると、その特定のタグの下にある投稿のみを表示する新しいページに移動します。これを行うには、バックエンドに新しいルートと処理ロジック、およびフロントエンドに対応するビューを作成する必要があります。
ステップ 1: サービスロジックの拡張
まず、tags_service.py
に2つの新しいメソッドを追加する必要があります。1つはタグIDでタグを検索するため (その名前を取得するため)、もう1つはタグIDで関連するすべての投稿を検索するためです。
tags_service.py
ファイルを開き、次のコンテンツを追加します。
# tags_service.py import uuid from typing import List from sqlmodel import Session, select from models import Tag, Post, PostTagLink # Post および PostTagLink をインポート def find_or_create_tags(tag_names: List[str], session: Session) -> List[Tag]: """ タグ名のリストに基づいてタグエンティティを検索または作成します。 """ # ... この関数は変更されません ... tags = [] if not tag_names: return tags statement = select(Tag).where(Tag.name.in_(tag_names)) existing_tags = session.exec(statement).all() tags.extend(existing_tags) existing_tag_names = {tag.name for tag in existing_tags} new_tag_names = [name for name in tag_names if name not in existing_tag_names] for name in new_tag_names: new_tag = Tag(name=name) session.add(new_tag) tags.append(new_tag) session.commit() for tag in tags: if tag.id is None: session.refresh(tag) return tags def get_tag_by_id(tag_id: uuid.UUID, session: Session) -> Tag | None: """IDで単一のタグを検索します""" return session.get(Tag, tag_id) def get_posts_by_tag_id(tag_id: uuid.UUID, session: Session) -> List[Post]: """タグIDで関連するすべての投稿を検索します""" statement = ( select(Post) .join(PostTagLink, Post.id == PostTagLink.post_id) .where(PostTagLink.tag_id == tag_id) .order_by(Post.createdAt.desc()) ) posts = session.exec(statement).all() return posts
コードの説明:
get_tag_by_id
: プライマリキーでタグオブジェクトを取得するためのシンプルなヘルパー関数です。get_posts_by_tag_id
: これはコアとなるクエリロジックです。SQLModel のselect
およびjoin
メソッドを使用して、PostTagLink
関連テーブルを介して指定されたtag_id
に関連付けられたすべてのPost
オブジェクトをフィルタリングし、作成時間で降順に並べ替えます。
ステップ 2: タグルートの作成
次に、/tags/{tag_id}
リクエストを処理するルートを実装しましょう。
まず、routers
フォルダ内に tags.py
という名前の新しいファイルを作成します。
# routers/tags.py import uuid from fastapi import APIRouter, Request, Depends from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from sqlmodel import Session from database import get_session from auth_dependencies import get_user_from_session import tags_service import tracking_service router = APIRouter() templates = Jinja2Templates(directory="templates") @router.get("/tags/{tag_id}", response_class=HTMLResponse) def get_posts_by_tag( request: Request, tag_id: uuid.UUID, session: Session = Depends(get_session), user: dict | None = Depends(get_user_from_session), ): # 1. このタグの下のすべての投稿を取得します posts = tags_service.get_posts_by_tag_id(tag_id, session) # 2. タグ情報を取得して、ページにタグ名を表示します tag = tags_service.get_tag_by_id(tag_id, session) # 3. ホームページとの一貫性を維持するために、投稿リストのビューカウントをバッチで取得します post_ids = [post.id for post in posts] view_counts = tracking_service.get_counts_by_post_ids(post_ids, session) for post in posts: post.view_count = view_counts.get(post.id, 0) tag_name = tag.name if tag else "Unknown" return templates.TemplateResponse( "posts-by-tag.html", { "request": request, "posts": posts, "user": user, "filter_name": tag_name, "title": f"Posts in {tag_name}", }, )
最後に、この新しいルーターモジュールを main.py
に含めることを忘れないでください。
# main.py # ... 他のインポート from routers import posts, users, auth, comments, uploads, tags # tags ルーターをインポート # ... # ルーターを含める app.include_router(posts.router) app.include_router(users.router) app.include_router(auth.router) app.include_router(comments.router) app.include_router(uploads.router) app.include_router(tags.router) # タグルーターをマウント
ステップ 3: フロントエンドビューの作成
最後のステップは、templates
フォルダに posts-by-tag.html
ビューファイルを作成することです。このファイルは、タグでフィルタリングされた投稿リストを表示するために使用され、その内容は index.html
と非常によく似ています。
templates/posts-by-tag.html
ファイルを作成します。
{% include "_header.html" %} <div class="filter-header"> <h2>Posts in Tag: <strong>{{ filter_name }}</strong></h2> </div> {% if posts %} <div class="post-list"> {% for post in posts %} <article class="post-item"> <h2><a href="/posts/{{ post.id }}">{{ post.title }}</a></h2> <p>{{ post.content[:150] }}...</p> <small>{{ post.createdAt.strftime('%Y-%m-%d') }} | Views: {{ post.view_count }}</small> </article> {% endfor %} </div> {% else %} <p>No posts found in this tag.</p> {% endif %} <a href="/" class="back-link" style="margin-top: 2rem;">← Back to Home</a> {% include "_footer.html" %}
このテンプレートは、動的にタイトル (例: "Posts in Tag: Python") とそのタグの下の投稿リストを表示します。タグの下に投稿がない場合は、対応するメッセージが表示されます。
実行とテスト
アプリケーションを再起動します。
uvicorn main:app --reload
ブラウザを開き、タグが付いた投稿に移動してから、投稿の下にあるいずれかのタグリンクをクリックします。
対応するタグのフィルターページにリダイレクトされ、そのタグの下にあるすべての投稿のリストが表示されます。
これらの2つのチュートリアルにより、ブログに完全なタグ付けシステムが追加されました。
この時点で、FastAPIブログプロジェクトは、基本的なインフラストラクチャからコア機能、コンテンツ編成、データ分析まですべてをカバーしています。
ブログ機能の可能性は無限です。現在のフレームワークに基づいて、さらに機能を追加し続けることができます。残りはあなたの想像力次第です!
Leapcell にデプロイすることを忘れないでください。FastAPIサポート、PostgreSQLデータベース、Redis、Web分析、およびWebアプリケーションの構築に必要なすべてのツールを提供しています。