Skip to content
Familiar asyncio ORM for python, built with relations in mind
Branch: master
Clone or download
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github pr/issue templates Aug 22, 2018
docs Fix typo: Entierly --> Entirely (#129) Apr 25, 2019
examples Added automatic MySQL connection retry, v0.11.9 Apr 18, 2019
tortoise Extra parameters now get passed through to the MySQL & PostgreSQL dri… May 15, 2019
.coveragerc Extra parameters now get passed through to the MySQL & PostgreSQL dri… May 15, 2019
.gitignore Started on tests Jun 30, 2018
.pylintrc Got rid of the easy linting code-quality issues Sep 25, 2018
.readthedocs.yml Fix ReadTheDocs to build using Python3.6 and install dependancies for… Jul 17, 2018
.travis.yml Converge and update config files (#123) Apr 4, 2019
CHANGELOG.rst Extra parameters now get passed through to the MySQL & PostgreSQL dri… May 15, 2019
CONTRIBUTORS.rst Added ability to create tables with "unique_together" meta options (#112 Mar 20, 2019
LICENSE.txt Initial commit Mar 29, 2018 Use find_packages instead of graft Jul 9, 2018
Makefile Converge and update config files (#123) Apr 4, 2019
README.rst Add gitter Apr 16, 2019 Black formatter integration (#120) Apr 3, 2019 [WIP] Sample Quart integration (#121) Apr 8, 2019
requirements-dev.txt Added automatic MySQL connection retry, v0.11.9 Apr 18, 2019 Read isolation, v0.11.5 (#111) Mar 20, 2019
requirements-pypy.txt Added automatic MySQL connection retry, v0.11.9 Apr 18, 2019
requirements.txt Update requirements to latest version of pypika with performance fixes Jan 24, 2019
setup.cfg [WIP] Sample Quart integration (#121) Apr 8, 2019 Converge and update config files (#123) Apr 4, 2019
tox.ini Converge and update config files (#123) Apr 4, 2019


Tortoise ORM


Tortoise ORM is an easy-to-use asyncio ORM (Object Relational Mapper) inspired by Django.

Tortoise ORM was build with relations in mind and admiration for the excellent and popular Django ORM. It's engraved in it's design that you are working not with just tables, you work with relational data.

You can find docs at ReadTheDocs


Tortoise ORM is young project and breaking changes without following semantic versioning are to be expected

Tortoise ORM is supported on CPython >= 3.5.3 for SQLite, MySQL and PostgreSQL, and PyPy3.5 >= 5.10 for SQLite and MySQL only.

Why was Tortoise ORM built?

Python has many existing and mature ORMs, unfortunately they are designed with an opposing paradigm of how I/O gets processed. asyncio is relatively new technology that has a very different concurrency model, and the largest change is regarding how I/O is handled.

However, Tortoise ORM is not first attempt of building asyncio ORM, there are many cases of developers attempting to map synchronous python ORMs to the async world, having to compromise heavily as those ORMs were not designed for an asynchronous event loop. Those few ORMs, which tried new approaches stabilized at point, where they lost word "relational" from ORM and mostly were limited to fetching rows from single table mapped to objects.

Hence we started Tortoise ORM.

Tortoise ORM was designed to be functional, yet familiar, to ease the migration of developers wishing to switch to asyncio.

How is an ORM useful?

When you build an application or service that uses a relational database, there is a point when you can't just get away with just using parameterized queries or even query builder, you just keep repeating yourself, writing slightly different code for each entity. Code has no idea about relations between data, so you end up concatenating your data almost manually. It is also easy to make a mistake in how you access your database, making it easy for SQL-injection attacks to occur. Your data rules are also distributed, increasing the complexity of managing your data, and even worse, is applied inconsistently.

An ORM (Object Relational Mapper) is desgined to address these issues, by centralising your data model and data rules, ensuring that your data is managed safely (providing immunity to SQL-injection) and keeps track of relationships so you don't have to.

Getting Started


First you have to install tortoise like this:

pip install tortoise-orm

Then you should install your db driver

pip install asyncpg aiosqlite

Quick Tutorial

Primary entity of tortoise is tortoise.models.Model. You can start writing models like this:

from tortoise.models import Model
from tortoise import fields

class Tournament(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()

    def __str__(self):

class Event(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
    tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
    participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team')

    def __str__(self):

class Team(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()

    def __str__(self):

After you defined all your models, tortoise needs you to init them, in order to create backward relations between models and match your db client with appropriate models.

You can do it like this:

from tortoise import Tortoise

async def init():
    # Here we connect to a SQLite DB file.
    # also specify the app name of "models"
    # which contain models from "app.models"
    await Tortoise.init(
        modules={'models': ['app.models']}
    # Generate the schema
    await Tortoise.generate_schemas()

Here we create connection to SQLite database in the local directory called db.sqlite3, and then we discover & initialise models.

Tortoise ORM currently supports the following databases:

  • SQLite
  • PostgreSQL (requires asyncpg)
  • MySQL (requires aiomysql)

generate_schema generates the schema on an empty database. Tortoise generates schemas in safe mode by default which includes the IF NOT EXISTS clause, so you may include it in your main code.

After that you can start using your models:

# Create instance by save
tournament = Tournament(name='New Tournament')

# Or by .create()
await Event.create(name='Without participants', tournament=tournament)
event = await Event.create(name='Test', tournament=tournament)
participants = []
for i in range(2):
    team = Team.create(name='Team {}'.format(i + 1))

# M2M Relationship management is quite straightforward
# (also look for methods .remove(...) and .clear())
await event.participants.add(*participants)

# You can query related entity just with async for
async for team in event.participants:

# After making related query you can iterate with regular for,
# which can be extremely convenient for using with other packages,
# for example some kind of serializers with nested support
for team in event.participants:

# Or you can make preemptive call to fetch related objects
selected_events = await Event.filter(
).prefetch_related('participants', 'tournament')

# Tortoise supports variable depth of prefetching related entities
# This will fetch all events for team and in those events tournaments will be prefetched
await Team.all().prefetch_related('events__tournament')

# You can filter and order by related models too
await Tournament.filter(
    events__name__in=['Test', 'Prod']


Please have a look at the Contribution Guide


This project is licensed under the Apache License - see the LICENSE.txt file for details

You can’t perform that action at this time.