What it is
Tortoise-ORM is an easy-to-use, asyncio-supporting Object-Relational Mapper (ORM) for Python, designed to provide a simple, familiar interface similar to Django ORM but fully asynchronous.
Tortoise-ORM allows you to define models as Python classes, perform asynchronous CRUD operations, manage relationships, and use querysets similar to Django ORM. It supports migrations via Aerich and integrates seamlessly with async web frameworks.
Installation
pip install tortoise-orm[aiohttp,asyncpg]Getting started
The smallest useful thing you can do with it, and what each part means.
from tortoise import Tortoise, fields, models
class User(models.Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=50)
email = fields.CharField(max_length=100, unique=True)import asyncio
async def init():
await Tortoise.init(db_url='sqlite://db.sqlite3', modules={'models': ['__main__']})
await Tortoise.generate_schemas()
asyncio.run(init())Advanced usage
Where the library earns its place over a simpler alternative.
async def create_user():
user = await User.create(name='Alice', email='alice@example.com')
print(user.id)
asyncio.run(create_user())async def get_users():
users = await User.filter(name='Alice')
for user in users:
print(user.name, user.email)
asyncio.run(get_users())async def update_user():
user = await User.get(id=1)
user.name = 'Bob'
await user.save()
asyncio.run(update_user())async def delete_user():
user = await User.get(id=1)
await user.delete()
asyncio.run(delete_user())class Post(models.Model):
id = fields.IntField(pk=True)
title = fields.CharField(max_length=100)
author = fields.ForeignKeyField('models.User', related_name='posts')async def get_user_posts():
user = await User.get(id=1)
posts = await user.posts.all()
for post in posts:
print(post.title)
asyncio.run(get_user_posts())Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- tortoise.exceptions.DoesNotExist
- Occurs when querying a model instance that does not exist. Use try/except or `.first()` to avoid exceptions.
- tortoise.exceptions.IntegrityError
- Raised when unique constraints or foreign key constraints are violated. Ensure input data respects constraints.
- OperationalError
- Occurs when the database connection fails. Check DB URL, network, or migrations.
Best practices
- Use async/await syntax consistently when interacting with the database.
- Define related_name for relationships for cleaner reverse lookups.
- Use Aerich for database migrations to track schema changes.
- Keep models modular and organized per app/module.
- Handle exceptions such as DoesNotExist and IntegrityError when performing CRUD operations.
Background
Why it exists, and what it was reacting to.
Tortoise-ORM was created to offer a lightweight, asynchronous ORM for Python developers using async frameworks like FastAPI and Starlette. It emphasizes simplicity, performance, and developer-friendly APIs, supporting multiple database backends including SQLite, PostgreSQL, and MySQL.
