DEV Community

Cover image for FastAPI with Django ORM and Admin
kathmandu
kathmandu

Posted on • Originally published at qiita.com

36 2

FastAPI with Django ORM and Admin

Introduction 😉

I'm Manato Kato, Japanese college student, and I love Python.
I'd like to introduce my new project "FastAPI with Django ORM and Admin".

Beginning 📍

Programming is a means to an end

The reason why I do programming is because I want to create something and give it shape. Therefore, I see programming as just a means to an end.

The first technology I learned

My first programming language was Python, and I've been doing contests and internships in Python ever since. And I often use Django when writing server-side code. This is because Django was the first framework I ever touched, and I used Django+DRF in my internship.

Disadvantages of Django

However, Django has some disadvantages.

Looking for a new framework

So I decided to touch a new framework.
As mentioned above, I did not want to learn a new language because what I want to do is create things and programming is just a means to an end. (I was trying to get introduced to the Go language at one point, but gave it up due to lack of time).
After much research, I learned that a framework called FastAPI was coming along quite well.

Advantages of FastAPI

When I touched FastAPI, it was a revolution.

  • I can include typedefs in Python.
    • I was very happy to see this feature, as I had a hard time with DX dropping due to the lack of types in Django development at my internship.
  • Ability to output API documentation by default.
    • Genius.
    • It's very effective in the division of labor with the frontend.
  • fast.

Disadvantages of FastAPI

There are a lot of advantages as mentioned above, but I still miss Django.

  • Admin Page.
    • This is still where the beauty of Django lies!
    • I really want it.
  • ORM
    • My hands have grown comfortable with Django's ORM.

Then...

Let's merge FastAPI and Django!!!

Code 👨🏻‍💻

https://github.com/kathmandu777/fastapi-django-template

Directory structure

├── README.md
├── docker-compose.yml
├── fastapi
│   ├── Dockerfile
│   ├── app
│   │   ├── __init__.py
│   │   ├── admin
│   │   │   ├── __init__.py
│   │   │   └── user.py
│   │   ├── api
│   │   │   ├── __init__.py
│   │   │   ├── auth.py
│   │   │   └── user.py
│   │   ├── apps.py
│   │   ├── dependencies
│   │   │   ├── __init__.py
│   │   │   └── auth.py
│   │   ├── migrations
│   │   │   ├── 0001_initial.py
│   │   │   └── __init__.py
│   │   ├── models
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   └── user.py
│   │   ├── routers
│   │   │   ├── __init__.py
│   │   │   ├── auth.py
│   │   │   ├── health.py
│   │   │   └── user.py
│   │   └── schemas
│   │       ├── __init__.py
│   │       ├── auth.py
│   │       └── user.py
│   ├── config
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── exceptions.py
│   │   ├── jwt.py
│   │   ├── log.py
│   │   ├── password.py
│   │   ├── settings
│   │   │   ├── base.py
│   │   │   ├── local.py
│   │   │   └── production.py
│   │   └── urls.py
│   ├── fastapi.env
│   ├── manage.py
│   ├── media
│   ├── poetry.lock
│   ├── pyproject.toml
│   ├── scripts
│   │   ├── runlocalserver.sh
│   │   └── startserver.sh
│   └── static
│       └── admin
├── poetry.lock
└── pyproject.toml
Enter fullscreen mode Exit fullscreen mode

I use Django as a reference, and use a separate style for each application.

  • models: Django ORM
  • routers: FastAPI routers
  • schemas: FastAPI Pydantic models
  • api: FastAPI view

mounts

"""
Django settings
"""
django_app = get_asgi_application()


"""
FastAPI settings
"""
from app.routers import auth_router, health_router, user_router

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

fastapi_app = FastAPI()

# routers
fastapi_app.include_router(health_router, tags=["health"], prefix="/health")

# to mount Django
fastapi_app.mount("/django", django_app)
fastapi_app.mount("/static", StaticFiles(directory="static"), name="static")
fastapi_app.mount("/media", StaticFiles(directory="media"), name="media")
Enter fullscreen mode Exit fullscreen mode

Create an asgi application for Django and FastAPI, and mount the Django application from the FastAPI application.

Models

Same as when creating in Django.

class User(AbstractBaseUser, PermissionsMixin, BaseModelMixin):
    objects = UserManager()

    email = models.EmailField(_("email address"), unique=True)

    MIN_LENGTH_USERNAME = 1
    MAX_LENGTH_USERNAME = 20
    username = models.CharField(
        _("username"),
        max_length=MAX_LENGTH_USERNAME,
    )

    # permissions
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    ...
Enter fullscreen mode Exit fullscreen mode

Schemas

from uuid import UUID

from pydantic import BaseModel


class ReadUserSchema(BaseModel):
    uuid: UUID
    username: str
    email: str

    class Config:
        orm_mode = True


class CreateUserSchema(BaseModel):
    username: str
    email: str
    password: str
Enter fullscreen mode Exit fullscreen mode

orm_mode = True is important.

Routers

Same as when creating in FastAPI.

user_router = APIRouter()


@user_router.get("/", response_model=ReadUserSchema)
async def get(request: Request, current_user: User = Depends(get_current_user)) -> User:
    return UserAPI.get(request, current_user)


@user_router.post(
    "/",
    response_model=ReadUserSchema,
)
async def create(request: Request, schema: CreateUserSchema) -> User:
    return await UserAPI.create(request, schema)
Enter fullscreen mode Exit fullscreen mode

API

class UserAPI:
    @classmethod
    def get(cls, request: Request, current_user: User) -> User:
        return current_user

    @classmethod
    async def create(cls, request: Request, schema: CreateUserSchema) -> User:
        user = await User.objects.filter(email=schema.email).afirst()
        if user:
            raise HTTPException(status_code=400, detail="Email already registered")
        schema.password = hash_password(schema.password)
        return await sync_to_async(User.objects.create)(**schema.dict())
Enter fullscreen mode Exit fullscreen mode

It is important to apply asynchronous processing to the DB using sync_to_async, afirst, etc. Synchronous processing will be very slow.

Asynchronous Processing in Django 4.1 🐍

Django 4.1 has been updated to allow asynchronous processing in the ORM, so you can now develop with a combination of FastAPI and Django like this. All of the reference articles below do not do asynchronous processing, so I expect them to be quite slow.

Finally 🏁

I am happy to build my ideal style. Since it is in the template repository, I hope you will use it.
As I am new to FastAPI, I am sure there are some weird things I am doing. Please send me Issue, PR.

References 📚

Using FastAPI with Django - Stavros' Stuff

https://www.stavros.io/posts/fastapi-with-django
https://qiita.com/Ningensei848/items/ac72ff6edf4d887cdcc1

My encounter with this article was the beginning of this project.

The integration of FastAPI and Django ORM

https://kigawas.me/posts/integrate-fastapi-and-django-orm/
https://github.com/kigawas/fastapi-django

Most helpful.

Learn to Use Django with FastAPI Frameworks

https://nsikakimoh.com/learn/django-and-fastapi-combo-tutorials

I found it while writing this article.
I think it differs from this project in that it uses wsgi to run Django and does not support async for the Django ORM.

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (1)

Collapse
 
ivansparq profile image
Ivan N

Thank you, it was usper useful.
I really like your repo and stole some of your precommit config :D

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay