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
How Can Custom Middleware Make Your FastAPI Unbreakable?

Crafting a Fortified Gateway for Your FastAPI Application

Blog Image
How Can You Create a Powerful RESTful API with Flask and SQLAlchemy?

Whip Up a RESTful API with Flask & SQLAlchemy: A Fun and Friendly Guide

Blog Image
How Can Serving Static Files in FastAPI Be This Effortless?

Unlocking the Ease of Serving Static Files with FastAPI

Blog Image
Can You Uncover the Secret Spells of Python's Magic Methods?

Diving Deep into Python's Enchanted Programming Secrets

Blog Image
Unlock Python's Memory Magic: Boost Speed and Save RAM with Memoryviews

Python memoryviews offer efficient handling of large binary data without copying. They act as windows into memory, allowing direct access and manipulation. Memoryviews support the buffer protocol, enabling use with various Python objects. They excel in reshaping data, network protocols, and file I/O. Memoryviews can boost performance in scenarios involving large arrays, structured data, and memory-mapped files.

Blog Image
Automating API Documentation in NestJS with Swagger and Custom Decorators

Automating API docs in NestJS using Swagger and custom decorators saves time, ensures consistency, and improves developer experience. Custom decorators add metadata to controllers and methods, generating interactive and accurate documentation effortlessly.