It seems that FastAPI has been growing recently with Momentum of Breaking Bamboo.
I want to flirt with FastAPI from Django, but I still want to continue using Django and its user system. It looks greedy, but it's actually such a good thing. This time I'll show you how to combine Django ORM with FastAPI.
Of course, there are also disadvantages. In terms of performance, ORM's Async IO support cannot be used. If you want to improve the performance, you can create a model such as orm separately.
First, let's talk about folder structure. You can create the contents of the folder based on the tutorial in the Django documentation. For details, see here.
$ django-admin startproject mysite
$ django-admin startapp poll
After generating the file, delete views.py
and models.py
and prepare a folder for FastAPI as shown below.
$ tree -L 3 -I '__pycache__|venv' -P '*.py'
.
├── manage.py
├── mysite
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── polls
├── __init__.py
├── adapters
│ └── __init__.py
├── admin.py
├── apps.py
├── migrations
│ └── __init__.py
├── models
│ └── __init__.py
├── routers
│ └── __init__.py
├── schemas
│ └── __init__.py
└── tests.py
7 directories, 15 files
Use of each folder properly:
--models
folder: Django ORM
--routers
folder: FastAPI routers
--schemas
folder: FastAPI Pydantic validator
--ʻAdapters` folder: Convert Django ORM to Pydantic validator
Refer to the Django document and try to enter the data.
>>> from polls.models import Choice, Question
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
>>> q.save()
For simplicity, put the example below in the folder __init__.py
. Also omit ʻimport`.
schemas
class FastQuestion(BaseModel):
question_text: str
pub_date: datetime
@classmethod
def from_model(cls, instance: Question):
return cls(
id=instance.id,
question_text=instance.question_text,
pub_date=instance.pub_date,
)
class FastQuestions(BaseModel):
items: List[FastQuestion]
@classmethod
def from_qs(cls, qs):
return cls(items=[FastQuestion.from_model(i) for i in qs])
adapters
ModelT = TypeVar("ModelT", bound=models.Model)
def retieve_object(model_class: Type[ModelT], id: int) -> ModelT:
instance = model_class.objects.filter(pk=id).first()
if not instance:
raise HTTPException(status_code=404, detail="Object not found.")
return instance
def retrieve_question(
q_id: int = Path(..., description="retrive question from db")
) -> Question:
return retieve_object(Question, q_id)
def retrieve_questions():
return Question.objects.all()
routers
@router.get("/")
def get_questions(
questions: List[Question] = Depends(adapters.retrieve_questions),
) -> FastQuestions:
return FastQuestions.from_qs(questions)
@router.get("/{q_id}")
def get_question(
question: Question = Depends(adapters.retrieve_question),
) -> FastQuestion:
return FastQuestion.from_model(question)
asgi.py
Added FastAPI App launch to mysite / asgi.py
.
from fastapi import FastAPI
from polls.routers import router
fastapp = FastAPI()
fastapp.include_router(router, tags=["questions"], prefix="/question")
First, create a static file for ʻuvicorn`:
$ python manage.py collectstatic --noinput
FastAPI is started with ʻuvicorn mysite.asgi: fastapp --reload, and Django is started with ʻuvicorn mysite.asgi: application --port 8001 --reload
.
Access http://127.0.0.1:8000/docs/
for the FastAPI Doc and http://127.0.0.1:8001/admin/
for the Django admin screen.
The combination of FastAPI and Django ORM is surprisingly easy, and if you divide the integration part well, you can create a neat folder structure.
The code above can be found on my Github.
Recommended Posts