python

[FastAPI] restapi를 위한 코드 순서

코드모헨 2024. 9. 26. 19:05

※ 프로젝트 구조 예시 

fastapi_project/  # 프로젝트 root 디렉토리
│
├── app/ #fast api 관련 api모음 
│   ├── __init__.py
│   ├── main.py # 여기서 app= FastAPI()를 선언한다. web framework의 시작점
│   ├── api/ 
│   │   ├── __init__.py
│   │   ├── endpoints/ #endpoint로 crud의 명령어가 어느 url로 갈지 정한다. api/와 같은 트리 레벨에서 router로 사용되는 경우도 많다
│   │   │   ├── __init__.py
│   │   │   ├── items.py #예시
│   │   │   └── users.py #예시
│   ├── core/ #django에서 setting에 해당하는 부분 프레임워크에 필요한 middleware 혹은 db를 설정하는 곳
│   │   ├── __init__.py
│   │   ├── config.py 
│   ├── models/ # orm에서 model을 맡는 부분
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── item.py
│   ├── crud/ # model에서 만들어진 객체를 schema를 이용하여 crud 명령어 구현
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── item.py
│   ├── schemas/ # model에 바로 웹에서 접근하는 것이 아닌 일종의 validation역할을 하는 부분 여기를 통해 model과 crud를 연결한다.
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── item.py
│   ├── db/ # db instance를 관리하는 부분
│   │   ├── __init__.py
│   │   ├── base.py # DB의 common model로 쓰일 base모델을 정의
│   │   └── session.py # db와 통신하는 엔진, 세션을 정의 그리고 db와 종속성 주입을 하는 함수를 정의
│   ├── tests/ # test코드 
│       ├── __init__.py
│       ├── test_items.py
│       └── test_users.py
│
├── .env
├── .gitignore
├── requirements.txt #가상환경
├── alembic.ini
├── README.md
└── alembic/
    ├── env.py # alembic orm 의 설정 파일
    ├── script.py.mako
    └── versions/ # alembic orm의 migration 관리

1. Main app 작성

  • FastAPI 인스턴스 할당 
  • 가장 마지막에 router 작성 (django에서 root urls.py의 역할을 한다

 

2. Config 작성

  • database url 및 어떤 db를 쓸지 엔진을 설정 
  • db종속성을 담당하는 Base모델을 이곳에서 정의해도 되고 db/database.py 와 db/session.py에 나눠서 작성해도 좋다

 

3. Model 작성

  • config 혹은 db/database에서 common 모델을 불러와 그걸 기반으로 모델을 만든다
class Item(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, index=True)
    description = Column(String, index=True)


4. Schema 작성

  • 외부에서 바로 db에 접속하는것이 아니라 스키마에 거쳐 validation을 확인한다.
  • 각 스키마의 Request, Response를 정의한다. (이 부분은 나중에 General한 request, response로 바꾸는걸 추천한다)
from typing import List, Optional, Generic, TypeVar
from pydantic import BaseModel, Field


T = TypeVar("T")


class ItemSchema(BaseModel):
    id: Optional[int] = None
    title: Optional[str] = None
    description: Optional[str] = None

    class Config:
        from_attributes = True


class Request(BaseModel, Generic[T]):
    parameter: Optional[T] = Field(...)


class RequestItem(BaseModel):
    parameter: ItemSchema = Field(...)


class Response(BaseModel, Generic[T]):
    code: str
    status: str
    message: str
    result: Optional[T] = None


5. CRUD

  • db에서 데이터를 불러온다 단 create update 같은 경우 schema를 통해 접근한다.
from sqlalchemy.orm import Session
from models.item import Item
from schemas.item import ItemSchema


def get_item(db: Session, skip: int = 0, limit: int = 100):
    return db.query(Item).offset(skip).limit(limit).all()


def get_item_by_id(db: Session, book_id: int):
    return db.query(Item)


def create_item(db: Session, item: ItemSchema):
    _item = Item(title=item.title, description=item.description)
    db.add(_item)
    db.commit()
    db.refresh(_item)
    return _item


def remove_book(db: Session, item_id: int):
    _item = get_item_by_id(db=db, item_id=item_id)
    db.delete(_item)
    db.commit()


def update_book(db: Session, item_id: int, title: str, description: str):
    _item = get_item_by_id(db=db, item_id=item_id)
    _item.title = title
    _item.description = description
    db.commit()
    db.refresh(_item)
    return _item


6. Router(혹은 api/endpoint)작성 

  • api의 밑 디렉토리 endpoint로 할거면 api/v1/endpoint로 하는걸 추천 한다. api의 버전을 관리하는 방법 중 하나다.
  • app.get과 router.get의 차이는 중앙에서 관리하는가 모듈에서 관리하는가 차이
    참고 - https://expressjs.com/ko/guide/routing.html
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from crud import item as crud_item
from db.session import get_db
from schemas import item as schemas_item

router = APIRouter()


@router.get("/")
async def read_items(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
    _item = crud_item.get_item(db, skip=skip, limit=limit)
    return schemas_item.Response(
        code="200", status="ok", message="Item read successfully", result=_item
    ).model_dump()


@router.post("/create")
async def create_item(request: schemas_item.RequestItem, db: Session = Depends(get_db)):
    crud_item.create_item(db, item=request.parameter)
    return schemas_item.Response(
        status="Ok", code="200", message="Item created successfully"
    )


7. 마무리

  • main 함수로 가서 router를 등록한다
app.include_router(items.router, prefix="/item", tags=["item"])

'python' 카테고리의 다른 글

Stable Diffusion 3 API 가지고 놀기  (3) 2024.09.27
poetry add 혹은 pipenv install 무한로딩  (0) 2024.05.08
class 적응하기 2  (0) 2024.01.30
class 적응하기 1  (0) 2024.01.30
Class method vs Static method in python  (2) 2024.01.03