Convert Figma logo to code with AI

tortoise logotortoise-orm

Familiar asyncio ORM for python, built with relations in mind

4,530
371
4,530
545

Top Related Projects

1,775

An async ORM. 🗃

1,383

A fast, user friendly ORM and query builder which supports asyncio.

20,398

Data validation using Python type hints

The Database Toolkit for Python

75,446

FastAPI framework, high performance, easy to learn, fast to code, ready for production

3,606

Pony Object Relational Mapper

Quick Overview

Tortoise ORM is an easy-to-use asyncio ORM (Object Relational Mapper) inspired by Django. It's designed to be simple, yet powerful, supporting Python 3.7+ and different databases like SQLite, PostgreSQL, and MySQL. Tortoise ORM allows developers to work with databases using Python objects, making database operations more intuitive and Pythonic.

Pros

  • Fully asynchronous, leveraging Python's asyncio capabilities
  • Supports multiple databases (SQLite, PostgreSQL, MySQL)
  • Intuitive API similar to Django ORM, making it easy for Django developers to transition
  • Includes a powerful query builder and supports complex queries

Cons

  • Relatively new compared to other ORMs, which may result in fewer resources and community support
  • Limited migration support compared to more established ORMs
  • May have a steeper learning curve for developers not familiar with asyncio concepts
  • Documentation could be more comprehensive for advanced use cases

Code Examples

  1. Defining a model:
from tortoise import fields
from tortoise.models import Model

class User(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=50)
    email = fields.CharField(max_length=100)

    def __str__(self):
        return self.name
  1. Creating and saving an instance:
async def create_user():
    user = await User.create(name="John Doe", email="john@example.com")
    print(f"User created: {user}")
  1. Querying the database:
async def get_users():
    users = await User.filter(name__startswith="J").order_by("name")
    for user in users:
        print(f"User: {user.name}, Email: {user.email}")
  1. Updating a record:
async def update_user(user_id: int, new_email: str):
    await User.filter(id=user_id).update(email=new_email)
    updated_user = await User.get(id=user_id)
    print(f"Updated user: {updated_user}")

Getting Started

To get started with Tortoise ORM, follow these steps:

  1. Install Tortoise ORM:

    pip install tortoise-orm
    
  2. Initialize your database in your main application file:

    from tortoise import Tortoise
    
    async def init():
        await Tortoise.init(
            db_url='sqlite://db.sqlite3',
            modules={'models': ['myapp.models']}
        )
        await Tortoise.generate_schemas()
    
    # In your main function or event loop
    await init()
    
  3. Define your models and start using Tortoise ORM in your application!

Competitor Comparisons

1,775

An async ORM. 🗃

Pros of ORM

  • Supports both sync and async operations, offering flexibility in usage
  • Designed to work seamlessly with FastAPI and Starlette frameworks
  • Provides a more Pythonic API with type hints and modern Python features

Cons of ORM

  • Less mature and potentially less stable compared to Tortoise ORM
  • Smaller community and fewer resources available for support
  • May have a steeper learning curve for developers familiar with SQLAlchemy-style ORMs

Code Comparison

Tortoise ORM:

from tortoise import fields, models

class User(models.Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=50)
    email = fields.CharField(max_length=100)

ORM:

import orm

class User(orm.Model):
    id = orm.Integer(primary_key=True)
    name = orm.String(max_length=50)
    email = orm.String(max_length=100)

Both ORMs offer similar syntax for defining models, but ORM's approach is more concise and closely resembles Python's built-in types. Tortoise ORM uses a separate fields module, while ORM integrates field types directly into the main module.

1,383

A fast, user friendly ORM and query builder which supports asyncio.

Pros of Piccolo

  • Built-in admin interface for easy database management
  • Support for async and sync queries, providing flexibility
  • Includes a schema generation tool for creating database migrations

Cons of Piccolo

  • Less mature and smaller community compared to Tortoise ORM
  • Limited documentation and fewer examples available
  • Steeper learning curve for developers new to the framework

Code Comparison

Piccolo example:

class User(Table):
    name = Varchar()
    age = Integer()

await User.insert(User(name="John", age=30))
users = await User.select().where(User.age > 25)

Tortoise ORM example:

class User(Model):
    name = fields.CharField(max_length=50)
    age = fields.IntField()

await User.create(name="John", age=30)
users = await User.filter(age__gt=25)

Both ORMs offer similar functionality for defining models and performing database operations. Piccolo uses a Table class for models, while Tortoise uses a Model class. Piccolo's query syntax is more SQL-like, whereas Tortoise follows a Django-inspired approach.

20,398

Data validation using Python type hints

Pros of Pydantic

  • More general-purpose data validation and settings management
  • Extensive type hinting and IDE support
  • Seamless integration with FastAPI for API development

Cons of Pydantic

  • Not specifically designed for ORM functionality
  • Lacks built-in database querying and management features

Code Comparison

Pydantic model:

from pydantic import BaseModel

class User(BaseModel):
    id: int
    name: str
    email: str

Tortoise ORM model:

from tortoise import fields
from tortoise.models import Model

class User(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=50)
    email = fields.CharField(max_length=100)

Key Differences

  • Pydantic focuses on data validation and serialization, while Tortoise-ORM is specifically designed for database operations
  • Tortoise-ORM provides asynchronous ORM capabilities, which Pydantic doesn't offer
  • Pydantic has broader applications beyond database management, including configuration management and API request/response modeling

Use Cases

  • Choose Pydantic for general data validation, API development with FastAPI, and configuration management
  • Opt for Tortoise-ORM when building asynchronous database-driven applications, especially with SQLite, PostgreSQL, or MySQL

The Database Toolkit for Python

Pros of SQLAlchemy

  • More mature and widely adopted, with extensive documentation and community support
  • Supports a broader range of databases and offers more advanced features
  • Provides both ORM and Core functionality, allowing for more flexibility in database interactions

Cons of SQLAlchemy

  • Steeper learning curve due to its extensive feature set and complex architecture
  • Can be slower in some scenarios compared to lighter-weight ORMs
  • Requires more boilerplate code for simple operations

Code Comparison

SQLAlchemy:

from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

Tortoise ORM:

from tortoise import Model, fields

class User(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=50)

    class Meta:
        table = "users"

Tortoise ORM offers a more concise and Pythonic syntax for model definition, while SQLAlchemy provides more explicit control over column types and table properties. SQLAlchemy's approach may be more familiar to developers coming from other languages or frameworks, while Tortoise ORM's syntax is designed to be more intuitive for Python developers.

75,446

FastAPI framework, high performance, easy to learn, fast to code, ready for production

Pros of FastAPI

  • Faster development with automatic API documentation (Swagger UI)
  • Built-in support for asynchronous programming
  • Integrates easily with other Python libraries and frameworks

Cons of FastAPI

  • Steeper learning curve for developers new to type hinting and async programming
  • Less mature ORM ecosystem compared to Tortoise ORM

Code Comparison

FastAPI example:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

Tortoise ORM example:

from tortoise import fields
from tortoise.models import Model

class User(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=50)

Key Differences

  • FastAPI is a web framework, while Tortoise ORM is an Object-Relational Mapping library
  • FastAPI focuses on API development, while Tortoise ORM specializes in database interactions
  • FastAPI has built-in validation and serialization, whereas Tortoise ORM requires additional libraries for these features

Use Cases

  • FastAPI: Ideal for building high-performance APIs with automatic documentation
  • Tortoise ORM: Best suited for projects requiring asynchronous database operations with a Pythonic ORM

Community and Ecosystem

  • FastAPI has a larger community and more third-party extensions
  • Tortoise ORM has a growing community focused on async database operations
3,606

Pony Object Relational Mapper

Pros of Pony

  • Intuitive and expressive query syntax using Python generators
  • Automatic schema generation and database creation
  • Built-in support for database transactions and optimistic locking

Cons of Pony

  • Smaller community and fewer resources compared to Tortoise ORM
  • Limited support for complex database operations and raw SQL queries
  • Steeper learning curve for developers new to its unique syntax

Code Comparison

Pony ORM:

from pony.orm import *

db = Database()

class Person(db.Entity):
    name = Required(str)
    age = Required(int)

db.generate_mapping(create_tables=True)

Tortoise ORM:

from tortoise import fields
from tortoise.models import Model

class Person(Model):
    name = fields.CharField(max_length=50)
    age = fields.IntField()

    class Meta:
        table = "persons"

Both ORMs provide object-oriented approaches to database interactions, but Pony ORM offers a more concise syntax for defining models. Tortoise ORM, on the other hand, follows a structure similar to Django ORM, which may be more familiar to some developers.

Pony ORM's query syntax is often praised for its readability and Pythonic nature, while Tortoise ORM provides better support for asynchronous operations and integration with FastAPI.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

============ Tortoise ORM

.. image:: https://img.shields.io/pypi/v/tortoise-orm.svg?style=flat :target: https://pypi.python.org/pypi/tortoise-orm .. image:: https://pepy.tech/badge/tortoise-orm/month :target: https://pepy.tech/project/tortoise-orm .. image:: https://github.com/tortoise/tortoise-orm/workflows/gh-pages/badge.svg :target: https://github.com/tortoise/tortoise-orm/actions?query=workflow:gh-pages .. image:: https://github.com/tortoise/tortoise-orm/actions/workflows/ci.yml/badge.svg?branch=develop :target: https://github.com/tortoise/tortoise-orm/actions?query=workflow:ci .. image:: https://coveralls.io/repos/github/tortoise/tortoise-orm/badge.svg :target: https://coveralls.io/github/tortoise/tortoise-orm .. image:: https://app.codacy.com/project/badge/Grade/844030d0cb8240d6af92c71bfac764ff :target: https://www.codacy.com/gh/tortoise/tortoise-orm/dashboard?utm_source=github.com&utm_medium=referral&utm_content=tortoise/tortoise-orm&utm_campaign=Badge_Grade

Introduction

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

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

You can find the docs at Documentation <https://tortoise.github.io>_

.. note:: Tortoise ORM is a young project and breaking changes are to be expected. We keep a Changelog <https://tortoise.github.io/CHANGELOG.html>_ and it will have possible breakage clearly documented.

Tortoise ORM is supported on CPython >= 3.8 for SQLite, MySQL and PostgreSQL and Microsoft SQL Server and Oracle.

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 the first attempt of building an asyncio ORM. While there are many cases of developers attempting to map synchronous Python ORMs to the async world, initial attempts did not have a clean API.

Hence we started Tortoise ORM.

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

It also performs well when compared to other Python ORMs. In our benchmarks <https://github.com/tortoise/orm-benchmarks>_, where we measure different read and write operations (rows/sec, more is better), it's trading places with Pony ORM:

.. image:: https://raw.githubusercontent.com/tortoise/tortoise-orm/develop/docs/ORM_Perf.png :target: https://github.com/tortoise/orm-benchmarks

How is an ORM useful?

When you build an application or service that uses a relational database, there is a point where you can't 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 mistakes in how you access your database, which can be exploited by SQL-injection attacks. Your data rules are also distributed, increasing the complexity of managing your data, and even worse, could lead to those rules being applied inconsistently.

An ORM (Object Relational Mapper) is designed 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 keeping track of relationships so you don't have to.

Getting Started

Installation

First you have to install Tortoise ORM like this:

.. code-block:: bash

pip install tortoise-orm

You can also install with your db driver (aiosqlite is builtin):

.. code-block:: bash

pip install tortoise-orm[asyncpg]

For MySQL:

.. code-block:: bash

pip install tortoise-orm[asyncmy]

For Microsoft SQL Server/Oracle (not fully tested):

.. code-block:: bash

pip install tortoise-orm[asyncodbc]

Quick Tutorial

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

.. code-block:: python3

from tortoise.models import Model
from tortoise import fields

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

    def __str__(self):
        return self.name


class Event(Model):
    id = fields.IntField(primary_key=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):
        return self.name


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

    def __str__(self):
        return self.name

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 the appropriate models.

You can do it like this:

.. code-block:: python3

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(
        db_url='sqlite://db.sqlite3',
        modules={'models': ['app.models']}
    )
    # Generate the schema
    await Tortoise.generate_schemas()

Here we create a connection to an SQLite database in the local directory called db.sqlite3. Then we discover and initialise the models.

Tortoise ORM currently supports the following databases:

  • SQLite (requires aiosqlite)
  • PostgreSQL (requires asyncpg)
  • MySQL (requires asyncmy)
  • Microsoft SQL Server/Oracle (requires asyncodbc)

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:

.. code-block:: python3

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

# 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 = await Team.create(name='Team {}'.format(i + 1))
    participants.append(team)

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

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

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


# Or you can make a preemptive call to fetch related objects
selected_events = await Event.filter(
    participants=participants[0].id
).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']
).order_by('-events__participants__name').distinct()

Migration

Tortoise ORM uses Aerich <https://github.com/tortoise/aerich>_ as its database migration tool, see more detail at its docs <https://github.com/tortoise/aerich>_.

Contributing

Please have a look at the Contribution Guide <docs/CONTRIBUTING.rst>_.

ThanksTo

Powerful Python IDE Pycharm <https://www.jetbrains.com/pycharm/>_ from Jetbrains <https://jb.gg/OpenSourceSupport>_.

.. image:: https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg :target: https://jb.gg/OpenSourceSupport

License

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