python

Ready to Supercharge Your FastAPI App with an Async ORM?

Tortoise ORM: A Robust Sidekick for Async Database Management in FastAPI

Ready to Supercharge Your FastAPI App with an Async ORM?

Building a FastAPI application? Well, you’re going to need to manage databases efficiently if you want to keep things fast and scalable. Let me tell you about a nifty tool you might want to use—Tortoise ORM. This ORM is designed to make your life easier, especially if you’re working with SQLite or PostgreSQL databases.

First off, why even bother with Tortoise ORM? Unlike traditional ORMs like SQLAlchemy, which are synchronous, Tortoise ORM is asynchronous. What does that mean for you? Basically, it leverages async/await syntax, making it a perfect companion to FastAPI’s async programming style. Faster performance and smooth operation—sounds like a win-win to me.

Now, want to integrate Tortoise ORM into your FastAPI application? It’s easier than you might think. Let’s break it down step-by-step.

First things first, you’ll need to install Tortoise ORM. On top of that, you’ll need an async driver for your chosen database. For SQLite, go with aiosqlite. For PostgreSQL, asyncpg is your bud.

pip install tortoise-orm aiosqlite

Now, let’s talk about configuration. Hooking up Tortoise ORM to your database involves using the register_tortoise function. For SQLite, it might look something like this:

from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise

app = FastAPI()

register_tortoise(
    app,
    db_url="sqlite://db.sqlite3",
    modules={"models": ["__main__"]},
    generate_schemas=True,
    add_exception_handlers=True
)

Cool, right? After that, it’s all about defining your models. These models are essentially blueprints for your database tables. They tell Tortoise ORM what data to store and how to structure it.

Here’s a simple model for a City:

from tortoise import fields
from tortoise.models import Model

class City(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(50)
    timezone = fields.CharField(50)

    class Meta:
        table_description = "Cities and their time zones"
        table = "cities"

Run your application, and Tortoise ORM will automatically generate the database schema. The database will align itself based on these models, thanks to generate_schemas=True.

Switching over to PostgreSQL? No problem. It’s a similar setup, but you need to install asyncpg:

pip install asyncpg

Then adjust your config like so:

from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise

app = FastAPI()

register_tortoise(
    app,
    db_url="postgres://user:password@localhost:5432/database",
    modules={"models": ["__main__"]},
    generate_schemas=True,
    add_exception_handlers=True
)

Alright, enough setup. Let’s dive into CRUD operations—Create, Read, Update, Delete. These are your basic building blocks for interacting with the database.

Want to create a new city?

from fastapi import FastAPI, HTTPException
from tortoise.queryset import QuerySet

app = FastAPI()

@app.post("/cities/")
async def create_city(city: City):
    await City.create(**city.dict())
    return {"message": "City created successfully"}

Need to read the list of cities?

@app.get("/cities/")
async def read_cities():
    cities = await City.all()
    return [{"id": city.id, "name": city.name, "timezone": city.timezone} for city in cities]

How about updating an existing city?

@app.put("/cities/{city_id}")
async def update_city(city_id: int, city: City):
    city_obj = await City.get(id=city_id)
    if not city_obj:
        raise HTTPException(status_code=404, detail="City not found")
    await city_obj.update_from_dict(city.dict())
    await city_obj.save()
    return {"message": "City updated successfully"}

And of course, deleting a city:

@app.delete("/cities/{city_id}")
async def delete_city(city_id: int):
    city_obj = await City.get(id=city_id)
    if not city_obj:
        raise HTTPException(status_code=404, detail="City not found")
    await city_obj.delete()
    return {"message": "City deleted successfully"}

Testing is crucial too. You don’t want your tests to mess with your production database, right? Use an in-memory database when running tests. This ensures everything is isolated.

Here’s a setup using pytest with an in-memory SQLite database:

import os
from typing import Generator
import pytest
from fastapi.testclient import TestClient
from tortoise.contrib.test import finalizer, initializer
from ..main import app

DB_URL = "sqlite://:memory:"

@pytest.fixture(scope="session")
def event_loop():
    return asyncio.get_event_loop()

@pytest.fixture(scope="session")
def client() -> Generator:
    initializer(db_url=DB_URL, modules=["users.models"])
    with TestClient(app) as c:
        yield c
    finalizer()

When your app needs to talk to more than one database, Tortoise ORM can handle that too. Just specify multiple connections in your configuration.

Here’s what it might look like:

register_tortoise(
    app,
    config={
        'connections': {
            'default': {
                'engine': 'tortoise.backends.asyncpg',
                'credentials': {
                    'host': 'localhost',
                    'port': '5432',
                    'user': 'tortoise',
                    'password': 'qwerty123',
                    'database': 'test',
                }
            },
            'another_db': {
                'engine': 'tortoise.backends.asyncpg',
                'credentials': {
                    'host': 'localhost',
                    'port': '5432',
                    'user': 'tortoise',
                    'password': 'qwerty123',
                    'database': 'another_test',
                }
            }
        },
        'apps': {
            'models': {
                'models': ['__main__'],
                'default_connection': 'default',
            }
        }
    },
    generate_schemas=True,
    add_exception_handlers=True
)

You can control which database to use for queries by leveraging the using method:

await City.all().using('another_db')

Alright, wrapping things up. Tortoise ORM proves to be a powerful and efficient tool for managing databases in FastAPI applications. Its async nature aligns perfectly with FastAPI, offering smooth and high-performance web development. SQLite for development? No problem. PostgreSQL for production? It’s got you covered. Tortoise ORM brings the flexibility and scalability you need to build robust applications. Dive in, try it out, and watch your FastAPI project take flight!

Keywords: FastAPI applications, manage databases efficiently, Tortoise ORM, SQLite, PostgreSQL, async/await syntax, FastAPI async programming, install Tortoise ORM, CRUD operations, high-performance web development



Similar Posts
Blog Image
Top Python Database Libraries: Simplify Your Data Operations

Discover Python's top database libraries for efficient data management. Learn to leverage SQLAlchemy, psycopg2, pymysql, and more for seamless database operations. Boost your coding skills now!

Blog Image
Is Your API Ready for Prime Time With FastAPI and SQLAlchemy?

Transforming Code into a Well-Oiled, Easily Maintainable Machine with FastAPI and SQLAlchemy

Blog Image
CQRS Pattern in NestJS: A Step-by-Step Guide to Building Maintainable Applications

CQRS in NestJS separates read and write operations, improving scalability and maintainability. It shines in complex domains and microservices, allowing independent optimization of commands and queries. Start small and adapt as needed.

Blog Image
Mastering Python's Asyncio: Unleash Lightning-Fast Concurrency in Your Code

Asyncio in Python manages concurrent tasks elegantly, using coroutines with async/await keywords. It excels in I/O-bound operations, enabling efficient handling of multiple tasks simultaneously, like in web scraping or server applications.

Blog Image
NestJS + AWS Lambda: Deploying Serverless Applications with Ease

NestJS and AWS Lambda offer a powerful serverless solution. Modular architecture, easy deployment, and scalability make this combo ideal for efficient, cost-effective application development without infrastructure management headaches.

Blog Image
Is Your Web App's Front Door Secure with OAuth 2.0 and FastAPI?

Cracking the Security Code: Mastering OAuth 2.0 with FastAPI for Future-Proof Web Apps