😸

【AI_7日目】FastAPI_1冊目

2024/09/08に公開

こんにちは投資ロウトです。

背景

AI開発ができるためにwebAPIについて理解を深めていきます。
※Tailwind CSSとkeycloakは現場でピンチになれば再開します。

CRUD処理

コントローラーはモデルとビューを接続するためにファイルが拡大しがちなので、CRUD処理だけこちらに記載すると良いとのことでした。

api/cruds/stock.py

from sqlalchemy.orm import Session

import api.models.stock as stock_model
import api.schemas.stock as stock_schema

# DBセッションとStockCreateスキーマを引数に取り、Stockモデルのインスタンスを返す関数
def create_stock(db: Session, stock: stock_schema.StockCreate) -> stock_model.Stock:
    stock = stock_model.Stock(**stock.dict())
    db.add(stock)
    db.commit()
    db.refresh(stock)
    return stock

ルーターも変更していく。

api/routers/stock.py

from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session

import api.cruds.stock as stock_crud
from api.db import get_db

from typing import List

import api.schemas.stock as stock_schema

router = APIRouter()

@router.post("/stocks", response_model=stock_schema.StockCreateResponse)
async def create_stock(stock_body: stock_schema.StockCreate, db: Session = Depends(get_db)):
    # CRUD操作を行うcreate_stock関数を呼び出し、DBに新しいstockを作成して結果を返す
    return stock_crud.create_stock(db, stock_body)

「orm_mode = True」の意味は、レスポンススキーマが暗黙的にORMからDBモデルを受け取って変換することを意味するとのこと。

api/schemas/stock.py

from pydantic import BaseModel, Field

class StockBase(BaseModel):
    # 共通するフィールドは親クラスに定義
    stock_name: str | None = Field(None, example="投資ロウト株式会社")

class StockCreate(StockBase):
    pass

class StockCreateResponse(StockCreate):
    id: int

    class Config:
        # orm_modeはDB操作の為に必要とのこと。
        orm_mode = True

class Stock(StockBase):
    id: int

    class Config:
        orm_mode = True

動作確認をする。まずはMySQLのレコードを確認

executeの実施

レコードの確認。きちんと登録されています。(2件登録しました)

read処理

read処理の追加

from sqlalchemy.orm import Session
from sqlalchemy import Select
from sqlalchemy.engine import Result

import api.models.stock as stock_model
import api.schemas.stock as stock_schema

# DBセッションを引数に取り、(id, stock_name)のタプルのリストを返す関数
def get_stocks(db: Session) -> list[tuple[int, str]]:
    result: Result = db.execute(Select(stock_model.Stock.id, stock_model.Stock.stock_name).select_from(stock_model.Stock))  
    return result.all()

ルーターにも処理を追加

from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session

import api.cruds.stock as stock_crud
from api.db import get_db

from typing import List

import api.schemas.stock as stock_schema

router = APIRouter()

@router.get("/stocks", response_model=List[stock_schema.Stock])
async def list_stocks(db: Session = Depends(get_db)):
    return stock_crud.get_stocks(db)

問題なく値も取得できていました。

Update

idから取得する処理と、stock_nameを書き換える処理を記載する。

api/cruds/stock.py

# 引数で渡されたstockIdに該当するレコードがないか確認し、その結果を返す
def get_stock(db: Session, stock_id: int) -> stock_model.Stock | None:
    result: Result = db.execute(Select(stock_model.Stock).filter(stock_model.Stock.id == stock_id))
    return result.scalars().first()

# 引数で渡されたstock_nameに書き換えて更新する。
def update_stock(db: Session, stock_create: stock_schema.StockCreate, original: stock_model.Stock) -> stock_model.Stock:
    original.stock_name = stock_create.stock_name
    db.add(original)
    db.commit()
    db.refresh(original)
    return original

対象のidがなければ404エラーを返し、あれば更新を実施する。

api/routers/stock.py

# PUTリクエストを受け付け、指定されたstock_idに基づいてstockを更新するエンドポイント。レスポンスにはStockCreateResponseスキーマを返す
@router.put("/stocks/{stock_id}", response_model=stock_schema.StockCreateResponse)
async def update_stock(stock_id: int, stock_body: stock_schema.StockCreate, db: Session = Depends(get_db)):
    stock = stock_crud.get_stock(db, stock_id)
    if stock is None:
        raise HTTPException(status_code=404, detail="Stock not found")
    return stock_crud.update_stock(db, stock_body, stock)

テストを実施する。まずはDBの再確認から

idが1から3までしかないので、4を指定すると

404のNot Foundが出る

次に2のレコードで更新をしてみる

getメソッド側でデータを確認してみると

きちんと更新されていました。

Delete

最後に削除処理、同じようにoriginalの取得処理は別関数で定義されているので、そちらを利用

api/cruds/stock.py

# 削除処理
def delete_stock(db: Session, original: stock_model.Stock) -> None:
    db.delete(original)
    db.commit()

ルーター側の処理も先ほどとほぼ同じ処理

@router.delete("/stocks/{stock_id}", response_model=None)
async def delete_stock(stock_id: int, db: Session = Depends(get_db)):
    stock = stock_crud.get_stock(db, stock_id)
    if stock is None:
        raise HTTPException(status_code=404, detail="Stock not found")
    stock_crud.delete_stock(db, stock)
    return None

では削除していきます。

消えましたね。

と一旦以上で学習を区切りたいと思います。焦らずコツコツ自分のペースでできればと思います。ご精読ありがとうございました。一歩ずついきたいと思います。

Discussion