컴퓨터 프로그래밍/FastAPI

[FastAPI] Repository 패턴 및 orm 적용

한33 2025. 3. 15. 12:51

✔️ Session 설정

connection.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

DATABASE_URL = "mysql+pymysql://root:todos@127.0.0.1:3306/todos"


engine = create_engine(DATABASE_URL, echo=True)
SessionFactory = sessionmaker(autocommit=False, autoflush=False, bind=engine)


def get_db():
    session = SessionFactory()
    try:
        yield session
    finally:
        session.close()

 

✔️ Repository 패턴

database > repository

from sqlalchemy import select
from sqlalchemy.orm import Session
from typing import List

from database.orm import ToDo

def get_todos(session: Session) -> List[ToDo]:
    return list(session.scalars(select(ToDo)))

 

Session 을 이용해서 get_todos 함수가 호출될 시에 ToDo Table 의 데이터를 전부 list 로 가져올 수 있도록 로직을 짜주었다.

 

✔️ main.py 적용

main.py

# 할 일 전체 조회
@app.get("/todos", status_code=200)
def get_todos_handler(
        order: str | None = None,
        session: Session = Depends(get_db),
):
    todos: List[ToDo] = get_todos(session=session)

    if order == "DESC":
        return todos[::-1]
    return todos

 


💡HTTP Response 설정

schema > response

from pydantic import BaseModel
from typing import List

class ToDoSchema(BaseModel):
    id: int
    contents: str
    is_done: bool

    class Config:
        from_attributes = True

class ListToDoResponse(BaseModel):
    todos: List[ToDoSchema]

 

main,py

# 할 일 전체 조회
@app.get("/todos", status_code=200)
def get_todos_handler(
        order: str | None = None,
        session: Session = Depends(get_db),
):
    todos: List[ToDo] = get_todos(session=session)

    if order == "DESC":
        return ListToDoResponse(
        todos=[ToDoSchema.from_orm(todo) for todo in todos[::-1]]
    )
    return ListToDoResponse(
        todos=[ToDoSchema.from_orm(todo) for todo in todos]
    )
# 할 일 단일 조회
@app.get("/todos/{todo_id}", status_code=200)
def get_todo_handler(
        todo_id: int,
        session: Session = Depends(get_db),
) -> ToDoSchema:

    todo: ToDo | None = get_todo_by_todo_id(session=session, todo_id=todo_id)

    if todo:
        return ToDoSchema.from_orm(todo)
    raise HTTPException(status_code=404, detail="ToDo Not Found")

 


💡ORM 적용 ver

repository.py

from sqlalchemy import select, delete
from sqlalchemy.orm import Session
from typing import List

from database.orm import ToDo

def get_todos(session: Session) -> List[ToDo]:
    return list(session.scalars(select(ToDo)))

def get_todo_by_todo_id(session: Session, todo_id: int) -> ToDo | None:
    return session.scalar(select(ToDo).where(ToDo.id == todo_id))

def create_todo(session: Session, todo: ToDo) -> ToDo:
    session.add(instance=todo)
    session.commit() # db 저장
    session.refresh(instance=todo) # id 를 가져오기 위함
    return todo

def update_todo(session: Session, todo: ToDo) -> ToDo:
    session.add(instance=todo)
    session.commit() # db 저장
    session.refresh(instance=todo) # id 를 가져오기 위함
    return todo

def delete_todo(session: Session, todo_id:int) -> None:
    session.execute(delete(ToDo).where(ToDo.id == todo_id))
    session.commit()

 

main.py

from fastapi import FastAPI, Body, HTTPException, Depends
from schema.request import CreateTodoRequest
from schema.response import ToDoListSchema, ToDoSchema
from sqlalchemy.orm import Session
from typing import List

from database.connection import get_db
from database.repository import get_todos, get_todo_by_todo_id, create_todo, update_todo, delete_todo

from database.orm import ToDo

app = FastAPI()

@app.get("/")
def health_check_handler():
    return {"ping":"pong"}


# 할 일 전체 조회
@app.get("/todos", status_code=200)
def get_todos_handler(
        order: str | None = None,
        session: Session = Depends(get_db),
):
    todos: List[ToDo] = get_todos(session=session)

    if order == "DESC":
        return ToDoListSchema(
        todos=[ToDoSchema.from_orm(todo) for todo in todos[::-1]]
    )
    return ToDoListSchema(
        todos=[ToDoSchema.from_orm(todo) for todo in todos]
    )

# 할 일 단일 조회
@app.get("/todos/{todo_id}", status_code=200)
def get_todo_handler(
        todo_id: int,
        session: Session = Depends(get_db),
) -> ToDoSchema:

    todo: ToDo | None = get_todo_by_todo_id(session=session, todo_id=todo_id)

    if todo:
        return ToDoSchema.from_orm(todo)
    raise HTTPException(status_code=404, detail="ToDo Not Found")


# 할 일 생성
@app.post("/todos", status_code=201)
def create_todo_handler(
        request: CreateTodoRequest,
        session: Session = Depends(get_db),
) -> ToDoSchema:
    todo = ToDo.create(request=request) # id None
    todo: ToDo = create_todo(session=session, todo=todo)    # id int
    return ToDoSchema.from_orm(todo)


# 할 일 수정
@app.patch("/todos/{todo_id}", status_code=200)
def update_todo_handler(
        todo_id: int,
        is_done: bool = Body(..., embed=True),
        session: Session = Depends(get_db),
):
    todo: ToDo | None = get_todo_by_todo_id(session=session, todo_id=todo_id)

    if todo:
        # update
        todo.done() if is_done else todo.undone()
        todo : ToDo = update_todo(session=session, todo=todo)
        return ToDoSchema.from_orm(todo)
    raise HTTPException(status_code=404, detail="ToDo Not Found")

# 할 일 삭제
@app.delete("/todos/{todo_id}", status_code=204)
def delete_todo_handler(
        todo_id: int,
        session: Session = Depends(get_db),
):
    todo: ToDo | None = get_todo_by_todo_id(session=session, todo_id=todo_id)

    if not todo:
        raise HTTPException(status_code=404, detail="ToDo Not Found")

    delete_todo(session=session, todo_id=todo_id)

💡 Refactoring

database > repository.py

router = APIRouter(prefix="/todos")


@router.get("", status_code=200)
def get_todos_handler(
        order: str | None = None,
        todo_repo: ToDoRepository = Depends(ToDoRepository),
):
    todos: List[ToDo] = todo_repo.get_todos()

    if order == "DESC":
        return ToDoListSchema(
        todos=[ToDoSchema.from_orm(todo) for todo in todos[::-1]]
    )
    return ToDoListSchema(
        todos=[ToDoSchema.from_orm(todo) for todo in todos]
    )

 

Depends 인젝션을 걸어놨기 때문에 request 요청이 들어왔을 때 ToDoRepository 를 주입해주게 된다.

 

api > todo.py

class ToDoRepository:
    def __init__(self, session: Session = Depends(get_db)):
        self.session = session

 

그럼 재귀적으로 호출이 가능하도록 get db 가 의존 주입이 되어있는데.

 

database > connection

def get_db():
    session = SessionFactory()
    try:
        yield session
    finally:
        session.close()

 

get_db 는 session 을 만들고 끝나면 닫아주는 역할을 한다.

'컴퓨터 프로그래밍 > FastAPI' 카테고리의 다른 글

[FastAPI] Redis 를 활용해 otp 기능 구현  (0) 2025.03.17
[Alembic] Alembic  (0) 2025.03.15
[FastAPI] DB 연결 및 orm 설정  (0) 2025.03.14
[FastAPI] Status_code Error 처리  (0) 2025.03.14
[FastAPI] CRUD  (0) 2025.03.13