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
6 Essential Python Web Scraping Libraries with Real-World Code Examples

Master 6 essential Python web scraping libraries with practical code examples. Learn Beautiful Soup, Scrapy, Selenium & more for efficient data extraction.

Blog Image
What If Building a FastAPI Asynchronous API Was Like Assembling a High-Performance Racing Car?

Building Your Asynchronous API Engine: FastAPI Meets Tortoise ORM

Blog Image
Can Streaming Responses Supercharge Your Web App Performance?

Effortlessly Stream Big Data with FastAPI: Master Asynchronous Responses for Optimal Performance

Blog Image
NestJS and gRPC: Building High-Performance Inter-Service Communication

NestJS and gRPC combine for high-performance microservices. NestJS offers modular architecture, while gRPC provides fast inter-service communication. Together, they enable efficient, scalable applications with streaming capabilities and strong testing support.

Blog Image
Harness the Power of Custom Marshmallow Types: Building Beyond the Basics

Custom Marshmallow types enhance data serialization, handling complex structures beyond built-in types. They offer flexible validation, improve code readability, and enable precise error handling for various programming scenarios.

Blog Image
Is Your FastAPI App Missing This Essential Trick for Database Management?

Riding the Dependency Injection Wave for Agile Database Management in FastAPI