FastAPIのトップページのルーティングの仕組み

はじめに

My Portfolioは、FastAPIを使用して開発しました。

本記事では、トップページのURL(http://127.0.0.1:8000/)にアクセスしてから、

画面がレンダリングされるまでのソースコードの処理の流れについて書いていきます。

トップページのURLにアクセスしたときにまず起こっていること

こちらのトップページにアクセスした時に、ソースコードでは何が起こっているのでしょうか

まずは、app/routers/top.pyのtop_pageメソッドで画面の取得処理を行っています

from fastapi import Depends, Request
from app import models
from sqlalchemy.orm import Session
from app.database import get_db

from fastapi import APIRouter
from fastapi.templating import Jinja2Templates

router = APIRouter()
templates = Jinja2Templates(directory="app/templates")

@router.get("/")
def top_page(
    request: Request,
    db: Session = Depends(get_db)
):
    
    user_id = request.session.get("user_id")
    role = request.session.get("role")

    is_logged_in = "user_id" in request.session

    works = (
        db.query(models.Work)
        .order_by(models.Work.id.desc())
        .limit(2)
        .all()
    )

    blogs = (
        db.query(models.Blog)
        .order_by(models.Blog.published_at.desc())
        .limit(2)
        .all()
    )    

    recent_careers = (
        db.query(models.Career)
        .order_by(models.Career.id.desc())
        .limit(2)
        .all()
    )    

    return templates.TemplateResponse(
        request,
        "top.html",
        {
            "works": works,
            "blogs": blogs,
            "recent_careers": recent_careers,
            "user_id": user_id,
            "is_logged_in": is_logged_in
        }
    )

トップページのURLをtop_pageメソッドに登録する

fastapiからAPIRouterをインポートして、routerとしてインスタンスを生成します。そして、top_pageメソッドにトップページのURLを登録します

from fastapi import APIRouter

router = APIRouter()

@router.get("/")
def top_page(
    request: Request,
    db: Session = Depends(get_db)
):

テンプレートに渡す情報を定義する

次に、トップページのテンプレートファイル(app/templates/top.html)に渡す情報を定義します

以下のような情報です

  • ログインしているかどうか(is_logged_in)
  • 制作物の情報(works)
  • 技術ブログの情報(blogs)

↓ ログインしているかどうか(is_logged_in)

from fastapi import Depends, Request

@router.get("/")
def top_page(
    request: Request,
    db: Session = Depends(get_db)
):
    
    user_id = request.session.get("user_id")

    is_logged_in = "user_id" in request.session

    return templates.TemplateResponse(
        request,
        "top.html",
        {
            "is_logged_in": is_logged_in
        }
    )

↓ 制作物の情報(works)

from fastapi import Depends, Request
from app import models
from sqlalchemy.orm import Session
from app.database import get_db

@router.get("/")
def top_page(
    request: Request,
    db: Session = Depends(get_db)
):
    
    works = (
        db.query(models.Work)
        .order_by(models.Work.id.desc())
        .limit(2)
        .all()
    )  

    return templates.TemplateResponse(
        request,
        "top.html",
        {
            "works": works,
        }
    )

↓ 技術ブログの情報

from fastapi import Depends, Request
from app import models
from sqlalchemy.orm import Session
from app.database import get_db

@router.get("/")
def top_page(
    request: Request,
    db: Session = Depends(get_db)
):
    
    blogs = (
        db.query(models.Blog)
        .order_by(models.Blog.published_at.desc())
        .limit(2)
        .all()
    )    

    return templates.TemplateResponse(
        request,
        "top.html",
        {
            "blogs": blogs,
        }
    )

テンプレート(top.html)に値が渡ってくる

app/templates/top.htmlに以下のような値が渡ってきます

  • ログインしているかどうか(is_logged_in)
  • 制作物の情報(works)
  • 技術ブログの情報(blogs)

↓ ログインしているかどうか(is_logged_in)

    {% if is_logged_in %}
      <a
        href="/works-page/new"
        class="btn-outline"
      >
        新規制作物を登録する
      </a>
    {% endif %}

↓ 制作物の情報(works)

<section class="top-section">
  <div class="section-header">
    <h2 class="section-title">
      最近の制作物
    </h2>
    <a href="/works-page" class="primary-button view-all-btn view-all-btn-pc">
      すべて見る
    </a>    
  </div>

  <div class="top-grid">
    {% for work in works %}

      <a
        href="/works-page/{{ work.id }}"
        class="work-card"
      >
        {% if work.image_url %}
          <img
            src="{% if work.image_url.startswith('/') %}{{ work.image_url }}{% else %}/{{ work.image_url }}{% endif %}"
            alt="{{ work.title }}"            
            class="work-thumbnail"
          >
        {% endif %}

        <h2>
          {{ work.title }}
        </h2>

        <p class="work-description">
          {{ work.description }}
        </p>

        <div class="tech-tags">
          {% for tech in work.tech_stack.split(",") %}
            <span class="tech-tag">
              {{ tech.strip() }}
            </span>
          {% endfor %}
        </div>
      </a>

    {% endfor %}
  </div>

↓ 技術ブログの情報(blogs)

<section class="top-section">
  <div class="section-header">
    <h2 class="section-title">
      最近の技術ブログ
    </h2>
    <a href="/blogs-page" class="primary-button view-all-btn view-all-btn-pc">
      すべて見る
    </a>    
  </div>

  <div class="top-grid">
    {% for blog in blogs %}
      <a
        href="/blogs-page/{{ blog.id }}"
        class="work-card"
      >
        <h2>{{ blog.title }}</h2>

        <div class="blog-meta">
            {% if blog.published_at %}
                <span>{{ blog.published_at }}</span>
            {% endif %}
        </div>        

        <p class="work-description">
          {{ blog.summary }}
        </p>

        {% if blog.tags %}
          <div class="tech-tags">
            {% for tag in blog.tags.split(",") %}
              <span class="tech-tag">
                {{ tag.strip() }}
              </span>
            {% endfor %}
          </div>
        {% endif %}
      </a>
    {% endfor %}
  </div>

図にするとこんな感じ(is_logged_inの場合)

①URLからtop_pageメソッドの処理が開始する

②is_logged_in(ログインしているかどうか)がトップページのテンプレート(top.html)に渡される

③ログインしているかどうかによって、新規制作物を登録するボタンの表示・非表示を制御している

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です