Django ORM에 FastAPI를 얹어보자

2
post-thumbnail

디장고요? 성능 생각하시면 씨장고 쓰셔야죠
長考하셔야 합니다

회사에서 Django를 쓰기로 해서 오랜만에 봤는데
장고 어드민이 생각보다 엄청나게 좋아져서 만족하고 찾아보고 있습니다.

(비교적)최근에 FastAPI 라는게 나오며
파이썬에서도 좋은 성능으로 백엔드를 사용할 수 있게 되어서
Django admin과 ORM을 FastAPI를 쓸 수 있는 방법을 찾아보았습니다.


Conda를 이용한 Python 설치

준비물

conda

conda create --channel anaconda --name python python
conda activate python

프로젝트 세팅

# 의존성 설치
pip install virtualenv Django

# 프로젝트 생성
django-admin startproject mysite
cd mysite

# virtualenv 세팅
virtualenv venv
. ./venv/bin/activate

# 의존성 세팅
# autopep8은 pylint를 사용하신다면 주석을 풀고 설치하세요
pip install Django fastapi "uvicorn[standard]" #autopep8
pip freeze > requirements.txt

FastAPI 세팅

mysite/routers.py

from fastapi import FastAPI


def register_routers(app: FastAPI):
    pass

mysite/asgi.py

import os

from django.core.asgi import get_asgi_application
from fastapi import FastAPI

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

application = get_asgi_application()
fastapp = FastAPI()


def init(app: FastAPI):
    from .routers import register_routers

    register_routers(app)


init(fastapp)

이렇게 세팅하고 아래 명령어를 입력하면 FastAPI가 실행이 됩니다.
하지만 세팅만 하고 아무것도 안했으니 뭘 해도 404가 뜨겠지만요.

uvicorn mysite.asgi:fastapp --reload

모델 생성

아래 명령어를 입력하면 items 라는 폴더가 만들어집니다.

python manage.py startapp items

mysite/settings.py 에 아래 내용을 추가해주시고

# ...

INSTALLED_APPS = [
    'items.apps.ItemsConfig',
    # ...
]

# ...

모델을 추가해줍니다.
items/models.py

from django.db import models


class Item(models.Model):
    name = models.CharField(max_length=10)

생성한 모델을 라우터에 연결해주고
items/routers.py

from fastapi import APIRouter

from .models import Item

router = APIRouter(prefix="/items", tags=["items"])


@router.get("")
def get_all_item():
    return [item.__dict__ for item in Item.objects.all()]

mysite/routers.py

from fastapi import FastAPI

from items.routers import router as items_router


def register_routers(app: FastAPI):
    app.include_router(items_router)

마이그레이션 후 샘플 데이터를 하나 집어넣고 나서

python manage.py makemigrations
python manage.py migrate
python manage.py dbshell "INSERT INTO items_item VALUES (1, 'foo');"

http://localhost:8000/items 에 들어가보면 아래 같은 결과가 나올겁니다.

[{"_state":{"adding":false,"db":"default"},"id":1,"name":"foo"}]

모델을 작성할 때 models.Model을 상속받도록 만들었는데,
부모 클래스에 _state 라는 값이 있기 때문에 들어온 것입니다.

제대로 우리가 원했던 모습으로 나오게 하기 위해서는 DTO를 만들어서
DTO로 변환한 뒤 응답을 내보내야 합니다.

DTO 작성

items/dto.py

from pydantic.dataclasses import dataclass


@dataclass
class CreateItemDTO:
    name: str


@dataclass
class ItemDTO:
    id: int
    name: str

DTO를 사용하는 Service를 작성하고
items/services.py

from items.models import Item
from items.dto import ItemDTO, CreateItemDTO


def get_all_item() -> list[ItemDTO]:
    return [ItemDTO(**item.__dict__) for item in Item.objects.all()]


def get_one_item(item_id: int) -> ItemDTO:
    return ItemDTO(**Item.objects.get(id=item_id).__dict__)


def create_item(dto: CreateItemDTO) -> ItemDTO:
    item = Item.objects.create(**dto.__dict__)
    item.save()
    return ItemDTO(**item.__dict__)

Router에 연결합니다.
items/routers.py

from fastapi import APIRouter

import items.services as services
from items.dto import ItemDTO, CreateItemDTO

router = APIRouter(prefix="/items", tags=["items"])


@router.get("", response_model=list[ItemDTO])
def get_all_item() -> list[ItemDTO]:
    return services.get_all_item()


@router.get("/{item_id}", response_model=ItemDTO)
def get_one_item(item_id: int) -> ItemDTO:
    return services.get_one_item(item_id)


@router.post("", response_model=ItemDTO)
def create_item(dto: CreateItemDTO) -> ItemDTO:
    return services.create_item(dto)

하는김에 create도 만들어봤는데, 잘 작동하는지 확인해봅시다.
아래 명령어를 터미널에 붙여넣으면 item이 하나 만들어질겁니다.

curl -X POST http://localhost:8000/items -H 'Content-Type: application/json' -d '{"name": "bar"}'
# response: {"id":2,"name":"bar"}

그리고 아까의 주소로 들어가보면
http://localhost:8000/items

[{"id":1,"name":"foo"},{"id":2,"name":"bar"}]

이건 우리가 원했던 모습이야!
원하던대로 깔끔하게 응답이 오는것을 확인할 수 있습니다.

하나만 조회하는것도 만들었으니 그것도 확인해보면
http://localhost:8000/items/1

{"id":1,"name":"foo"}

이렇게 잘 나오는것을 확인할 수 있죠.

Swagger

FastAPI는 swagger 문서를 자동으로 만들어주는데,
아래의 주소로 들어가면 swagger 문서를 보실 수 있습니다.

http://localhost:8000/docs

총평

FastAPI에는 Tortoise ORM을 써야 한다고 하던데,
그런 조합을 써서는 Django Admin같은 편리한게 나오지 않잖아유?

fastapi-admin 이란게 있지만 이건 Django Admin만큼 편하지 않다보니
다소 성능이 떨어지더라도 Django ORM을 붙여서 써보고 싶었습니다.

다른 언어에도 이런 편한거 많은거 알고있다능

그리고 파이썬을 엄청 오랜만에 다시 접했는데(대략 6년)
타입도 선언이 가능하고, 언어 자체의 성능도 꽤나 많이 올라서
옛날처럼 사용하면서 고통스럽진 않겠다 하는 생각이 듭니다.

profile
지상 최강의 개발자 쥬니니

0개의 댓글