【FastAPI】リレーションモデルとレスポンス【SQLAlchemy】

今回は、FastAPISQLAlchemyを使用してデータベース作成時に

  • どのようにリレーション関係の持つモデルを作成するのか
  • スキーマを定義して、レスポンス形式をどのように指定するのか

について記載いたします。

データベースの作成方法やCRUD操作は下記記事をご参照ください。

この記事のサンプルコード

目次

リレーション関係のモデル作成

まずはデータベースのモデルを作成します。今回は、UserArticleモデルを作成して

  • ArticleにはUserを紐づけ
  • Userからは自分のArticleを一緒に取得できるようにする

この2つを考えてみたいと思います。db/models.pyを作成します。

from sqlalchemy.orm import relationship
from sqlalchemy.sql.schema import ForeignKey
from db.database import Base
from sqlalchemy import Column
from sqlalchemy.sql.sqltypes import Boolean, Integer, String

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String)
    email = Column(String)
    password = Column(String)
    is_active = Column(Boolean, default=True)
    articles = relationship("Article", back_populates="user")

class Article(Base):
    __tablename__ = 'articles'
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String)
    content = Column(String)
    is_display = Column(Boolean)
    creater_id = Column(Integer, ForeignKey('users.id'))
    user = relationship("User", back_populates="articles")

Articleには

creater_id = Column(Integer, ForeignKey('users.id'))

ForeignKeyを使用してユーザーモデルと紐づけを行いました。また、各モデルから参照できるようにrelationshipを記載しています。

articles = relationship("Article", back_populates="user")
user = relationship("User", back_populates="articles")

ForeignKeyなどはデータ削除時の挙動を指定するon_deleteなど様々な引数を持つのでぜひ確認してみてください。

https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html

スキーマの作成

リクエスト、レスポンス用のスキーマの作成を行います。

from typing import List
from pydantic import BaseModel

class Article(BaseModel):
    title: str
    content: str
    is_display: bool
    class Config():
        orm_mode = True

class UserBase(BaseModel):
    username: str
    email: str
    password: str

class UserDisplay(BaseModel):
    id: int
    username: str
    email: str
    articles: List[Article] = []
    class Config():
        orm_mode = True

class User(BaseModel):
    id: int
    username: str
    class Config():
        orm_mode = True

class ArticleBase(BaseModel):
    title: str
    content: str
    is_display: bool
    creater_id: int

class ArticleDisplay(BaseModel):
    title: str
    content: str
    is_display: bool
    user: User
    class Config():
        orm_mode = True

ポイントはarticles: List[Article] = []にてUserに紐づくArticleを紐づけています。

class UserDisplay(BaseModel):
    username: str
    email: str
    articles: List[Article] = []
    class Config():
        orm_mode = True

またArticleDisplayには、user: Userとクラスを指定しています。

class ArticleDisplay(BaseModel):
    title: str
    content: str
    is_display: bool
    user: User
    class Config():
        orm_mode = True

関数の作成

ユーザーと記事の作成・追加用関数を作成します。今回は更新・削除については省略します。

ユーザーの作成・取得

from db.hash import Hash
from sqlalchemy.orm.session import Session
from schemas import UserBase
from db.models import User


def create_user(db: Session, request: UserBase):
    new_user = User(
        username = request.username,
        email = request.email,
        password = Hash.get_password_hash(request.password)
    )
    db.add(new_user)
    db.commit()
    db.refresh(new_user)
    return new_user

def get_user(db: Session, id: int):
    return db.query(User).filter(User.id == id).first()

パスワードはハッシュ化していますが、ハッシュ化については下記記事を参照ください!

記事の作成・取得

from sqlalchemy.orm.session import Session
from schemas import ArticleBase
from db.models import Article

def create_article(db: Session, request: ArticleBase):
    new_article = Article(
        title = request.title,
        content = request.content,
        is_display = request.is_display,
        user_id = request.creater_id
    )
    db.add(new_article)
    db.commit()
    db.refresh(new_article)
    return new_article

def get_article(db: Session, id: int):
    article = db.query(Article).filter(Article.id == id).first()
    return article

エンドポイントの作成

先程作成した関数を用いてエンドポイントを作成します。

ユーザー – User

from typing import List
from schemas import UserDisplay, UserBase
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from db.database import get_db
from db import db_user


router = APIRouter(
  prefix='/user',
  tags=['user']
)

@router.post('/', response_model=UserDisplay)
def create_user(request: UserBase, db: Session = Depends(get_db)):
  return db_user.create_user(db, request)

@router.get('/{id}', response_model=UserDisplay)
def get_user(id: int, db: Session = Depends(get_db)):
    return db_user.get_user(db, id)

記事 – Article

from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from db.database import get_db
from db import db_article
from schemas import ArticleBase, ArticleDisplay

router = APIRouter(
    prefix='/article',
    tags=['article']
)

@router.post('/', response_model=ArticleDisplay)
def create_article(request: ArticleBase, db: Session = Depends(get_db)):
    return db_article.create_article(db, request)

@router.get('/{id}', response_model=ArticleDisplay)
def get_article(id: int, db: Session = Depends(get_db)):
    return db_article.get_article(db, id)

エンドポイントの確認

作成したエンドポイントからレスポンスを確認してみましょう。今回作成したものは

  • create_user ユーザーの追加
  • get_user ユーザーの1件取得
  • create_article 記事の追加
  • get_article 記事の1件確認

になります。

create_user

{
  "id": 0,
  "username": "string",
  "email": "string",
  "articles": []
}

get_user

{
  "id": 1,
  "username": "string",
  "email": "string",
  "articles": [
    {
      "title": "title1",
      "content": "content1",
      "is_display": true
    },
    {
      "title": "title2",
      "content": "content2",
      "is_display": true
    }
  ]
}

create_article

{
  "title": "title2",
  "content": "content2",
  "is_display": true,
  "user": {
    "id": 1,
    "username": "string"
  }
}

get_article

{
  "title": "title1",
  "content": "content1",
  "is_display": true,
  "user": {
    "id": 1,
    "username": "string"
  }
}

無事、作成出来ました!

この記事のサンプルコード

参考

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!

コメント

コメントする

目次
閉じる