
Tortoise ORM
An async-native Python ORM with a Django-style API: models as classes, await Model.filter(...) queries, and built-in migrations since 1.0.
- What it is
- Object-relational mapping (full orms)
- Type
- Open source
- Made by
- Tortoise ORM contributors
- License
- Apache 2.0
- Facts checked
- 9 October 2026
What it is
Tortoise ORM was built for asyncio applications. You define models by subclassing Model and declaring fields, including ForeignKeyField and ManyToManyField relations, and every database call is awaited: await Order.create(...), await obj.save(), await Order.filter(amount__gt=100). Filters use Django’s double-underscore style (name__icontains, customer__name) and can follow relations into joins. Related objects aren’t loaded behind your back; you await them or load them up front with fetch_related and prefetch_related.
It runs on CPython 3.10 and later and talks to SQLite (aiosqlite), PostgreSQL (asyncpg or psycopg), MySQL and MariaDB (asyncmy or aiomysql), and Microsoft SQL Server and Oracle through asyncodbc. Version 1.0, released in February 2026, added a native migration framework (tortoise makemigrations, tortoise migrate, tortoise sqlmigrate) that replaces the separate Aerich tool, moved all ORM state into explicit TortoiseContext objects, and added PostgreSQL full-text search. Contrib modules register Tortoise in a FastAPI app’s lifespan and generate Pydantic models from Tortoise models with pydantic_model_creator.
Why teams pick it
- Async from the ground up: every query is awaited, and a contrib module wires it into a FastAPI app’s lifespan
- Familiar to Django users: model classes,
filterandexcludewithfield__lookuparguments, and Django-like migration commands - Migrations are built in since 1.0, with autodetection, reversible operations,
RunPythonandRunSQL, and a command to preview the SQL - Small API surface for CRUD-heavy services: create, save, filter and prefetch without sessions to manage
Watch out for
- Async only: synchronous scripts or frameworks need an event loop around every call (
run_asyncorasyncio.run) - Version 1.0 (February 2026) broke existing code: Python 3.10+,
use_tzon by default, a new context-first architecture and removal of the old test helpers - A smaller ecosystem: about 5,600 GitHub stars against more than 12,000 for SQLAlchemy, which also does async through
AsyncSessionand supports external dialects - Its built-in migrations are new; projects started before 1.0 used Aerich, which the docs now call a legacy alternative
Alternatives
What all full orms have in common →How it fits with other technologies
Models, a filtered query across a relation, and an explicit prefetch (SQLite in memory) · python
from tortoise import Tortoise, fields, run_async
from tortoise.models import Model
class Customer(Model):
id = fields.IntField(primary_key=True)
name = fields.CharField(max_length=100)
class Order(Model):
id = fields.IntField(primary_key=True)
amount = fields.IntField()
customer = fields.ForeignKeyField("models.Customer", related_name="orders")
async def main():
await Tortoise.init(db_url="sqlite://:memory:", modules={"models": ["__main__"]})
await Tortoise.generate_schemas() # dev only; use `tortoise makemigrations` in production
ada = await Customer.create(name="Ada")
await Order.create(amount=120, customer=ada)
await Order.create(amount=80, customer=ada)
big = await Order.filter(amount__gt=100, customer__name="Ada").count()
print(big) # 1
await ada.fetch_related("orders") # relations load only when you await them
print([o.amount for o in ada.orders]) # [120, 80]
await Tortoise.close_connections()
run_async(main())Tortoise ORM and SQLAlchemy#
SQLAlchemy has supported asyncio since version 1.4, so async alone is no longer a reason to pick Tortoise. The difference is the API style and how much each one covers.
| Tortoise ORM | SQLAlchemy | |
|---|---|---|
| Style | Django-like models; await Model.filter(field__lookup=...) | Mapped classes used through a Session, plus Core for building SQL expressions |
| Sync and async | Async only | Sync, plus AsyncSession and async engines since 1.4 |
| Loading relations | Await the relation or fetch_related / prefetch_related | Eager loading, or awaitable_attrs under asyncio, where implicit lazy loading would fail |
| Migrations | Built in since 1.0 (tortoise makemigrations) | Alembic, a separate project from the SQLAlchemy authors |
| Databases | SQLite, PostgreSQL, MySQL and MariaDB, SQL Server, Oracle | The same five built in, plus external dialects |
See it in a real stack
No architecture uses Tortoise ORM yet. Drop it into the playground and wire it up yourself.Open playground →Where it came from2018–2026
Andrey Bondar published Tortoise ORM in March 2018. The goal, as the README still puts it, was a lightweight, async-native object-relational mapper for Python “with a familiar Django-like API”. Nickolas Grigoriadis and Jinlong Peng have since made the most commits.
Version 0.16.0 in March 2020 added automatic Pydantic models built from Tortoise models. Migrations stayed outside the core: Aerich, a separate tool, was integrated into the install in October 2020. Tortoise 1.0 arrived in February 2026 after almost eight years of 0.x releases, with native migrations, explicit contexts in place of global state, and database schema support for PostgreSQL and SQL Server.
Why it’s built this way
- Copy Django’s API, not SQLAlchemy’s
- The docs describe Tortoise as “inspired by Django”: model classes, querysets and double-underscore lookups such as
name__icontainscarry over, so the main new thing to learn isawait. - Async-only
- Every database call returns an awaitable, so there is one code path, built for
asyncioframeworks, instead of a sync API with async bolted on. - No implicit lazy loading
- Loading a relation means a database round trip, which in async code has to be awaited. Tortoise makes that explicit with awaitable relations and
fetch_related. - Migrations moved into the core in 1.0
- Migrations used to live in Aerich, a separate tool. The 1.0 framework autodetects model changes with
makemigrationsandmigratecommands, and Aerich’s own README now sends 1.0 users to it.
Timeline
- 2018
Andrey Bondar releases Tortoise ORM 0.1.0 in March
- 2020
0.16.0 in March builds Pydantic models from Tortoise models
- 2020
Aerich migrations integrated into the install in October
- 2026
Tortoise ORM 1.0 in February adds native migrations
Sources13 · facts checked 9 October 2026
- Tortoise ORM README (databases, drivers, Python versions, migrations)
- Tortoise ORM docs: Introduction
- Tortoise ORM docs: Query API (filters, fetch_related, prefetch_related)
- Tortoise ORM docs: Models
- Tortoise ORM docs: Migrations (built-in CLI; Aerich is a legacy alternative)
- Tortoise ORM docs: FastAPI integration (RegisterTortoise)
- Tortoise ORM docs: Pydantic serialisation (pydantic_model_creator)
- Tortoise ORM changelog (1.0.0 breaking changes and additions)
- Aerich README (use the built-in CLI for tortoise-orm 1.0 and later)
- SQLAlchemy 2.0 docs: Asynchronous I/O (asyncio), added in 1.4
- SQLAlchemy 2.0 docs: Dialects
- Alembic docs: Front matter (a project of the SQLAlchemy organization)
- sqlalchemy/sqlalchemy on GitHub