Qual ORM usar no seu próximo projeto Python?
Tags: python, database, beginners
Published: 2023-01-13 22:46:14


## O que é?

ORM (_Object-Relational Mappers_) é uma ferramenta fundamental para qualquer desenvolvedor que trabalha com bancos de dados relacionais. Ele permite acessar os dados usando a **sintaxe de objetos** em vez de SQL, tornando o código **mais limpo** e fácil de dar **manutenção**.

## Para que serve?

Ele separa a lógica de banco de dados da lógica de negócios. Além disso, ele permite que você trabalhe com dados em um formato nativo de **objetos Python**, o que é mais intuitivo e fácil de trabalhar.

## Quais são?

Quando se trata de escolher um ORM, existem várias opções para Python. As mais populares são:

- [SQLAlchemy](https://docs.sqlalchemy.org/en/latest/)
- [Django ORM](https://docs.djangoproject.com/en/4.1/ref/databases/)
- [peewee](https://docs.peewee-orm.com/en/latest/)
- [Pony ORM](https://docs.ponyorm.org/)
- [Tortoise-ORM](https://tortoise-orm.readthedocs.io/en/latest/)

## Como escolher?

Ao escolher um ORM, é importante considerar suas necessidades específicas e avaliar as características das opções disponíveis.

Aqui, vamos comparar brevemente os principais ORMs disponíveis para Python e discutir as características, vantagens e desvantagens, para ajudá-lo a escolher a melhor opção para seu projeto.

---

## peewee

![peewee logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y3iw02lw3trtp6enrtz6.png)

[peewee](https://docs.peewee-orm.com/en/latest/) é uma escolha popular para projetos de pequeno e médio porte, oferece uma **abstração simples** e intuitiva do banco de dados, e é **fácil** de usar e aprender. Ele é uma boa escolha para **projetos menores** e mais simples, e pode ser uma boa opção para **desenvolvedores iniciantes** que estão começando a trabalhar com ORMs.

```bash
$ pip install peewee
```

```py
from datetime import datetime

from peewee import *

# define a conexão com o banco de dados
db = SqliteDatabase(":memory:")

# define a classe do modelo
class User(Model):
    id = IntegerField(primary_key=True)
    name = TextField()
    created_on = DateTimeField(default=datetime.now)

    class Meta:
        database = db


# cria a tabela
db.create_tables([User])
# cria um novo usuário
user = User(name="John Doe")
user.save()
# busca um usuário pelo id
user = User.get(User.id == 1)
print(user.name)
# atualiza o nome do usuário
user.name = "Jane Doe"
user.save()
# deleta o usuário
user.delete_instance()
```

## Pony ORM

![Pony ORM logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fz6r9ym1swt7vizdwor3.png)

[Pony ORM](https://docs.ponyorm.org/) é muito semelhante ao [peewee](https://docs.peewee-orm.com/en/latest/), uma escolha para **projetos menores** e mais **simples**, e boa opção para **desenvolvedores iniciantes**.

```bash
$ pip install pony
```

```py
from datetime import datetime

from pony.orm import *

# define a conexão com o banco de dados
db = Database()
db.bind(provider="sqlite", filename=":memory:")

# define a classe do modelo
class User(db.Entity):
    id = PrimaryKey(int, auto=True)
    name = Required(str)
    created_on = Required(datetime, default=datetime.now)


# cria as tabelas no banco de dados
db.generate_mapping(create_tables=True)

with db_session:
    # cria um novo usuário
    user = User(name="John Doe")
    commit()
    # busca um usuário pelo id
    user = User[1]
    print(user.name)
    # atualiza o nome do usuário
    user.name = "Jane Doe"
    commit()
    # deleta o usuário
    user.delete()
    commit()
```

## SQLAlchemy

![SQLAlchemy logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l1c9uil2au07qs61wq9l.jpg)

[SQLAlchemy](https://docs.sqlalchemy.org/en/latest/) é considerado um dos ORMs mais **maduros e completos** disponíveis, oferecendo uma grande variedade de recursos para lidar com bancos de dados. Ele é uma boa escolha para projetos de médio e grande escala.

```bash
$ pip install sqlalchemy
```

```py
from datetime import datetime

from sqlalchemy import Column, DateTime, Integer, Text, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

# define a conexão com o banco de dados
engine = create_engine("sqlite:///:memory:")

# define a classe do modelo
Base = declarative_base()


class User(Base):
    __tablename__ = "user"
    id = Column(Integer, primary_key=True)
    name = Column(Text)
    created_on = Column(DateTime, default=datetime.utcnow)


# cria as tabelas no banco de dados
Base.metadata.create_all(bind=engine)
# cria uma sessão com o banco de dados
Session = sessionmaker(bind=engine)

with Session as session:
    # cria um novo usuário
    user = User(name="John Doe")
    session.add(user)
    session.commit()
    # busca um usuário pelo id
    user = session.query(User).get(1)
    print(user.name)
    # atualiza o nome do usuário
    user.name = "Jane Doe"
    session.commit()
    # deleta o usuário
    session.delete(user)
```

## Tortoise-ORM

![Tortoise-ORM logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3z0p2ry6htiut2vn0c2h.png)

[Tortoise-ORM](https://tortoise-orm.readthedocs.io/en/latest/) é construído sobre o `asyncio` e é projetado para aplicações que precisam de alta performance e escalabilidade, especialmente quando se trata de operações assíncronas. É uma boa escolha para projetos que exigem requisitos de concorrência e escalabilidade. Ele é fácil de usar e aprender e oferece uma abstração simples e intuitiva do banco de dados.

```bash
$ pip install tortoise-orm
```

O Tortoise-orm utiliza funções `await` para as operações no banco de dados. Logo, deve ser executado dentro de uma função `async`.

```py
# __main__.py
from datetime import datetime

from tortoise import Tortoise, fields, run_async
from tortoise.models import Model


# define a classe do modelo
class User(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
    created_on = fields.DatetimeField(default=datetime.now)


async def run():
    # define a conexão com o banco de dados
    await Tortoise.init(
        db_url="sqlite://:memory:",
        modules={"models": ["__main__"]},
    )
    # cria as tabelas no banco de dados
    await Tortoise.generate_schemas()
    # cria um novo usuário
    user = await User.create(name="John Doe")
    # busca um usuário pelo id
    user = await User.get(id=1)
    print(user.name)
    # atualiza o nome do usuário
    user.name = "Jane Doe"
    await user.save()
    # deleta o usuário
    await user.delete()


run_async(run())
```

---

## Comparativo: versões do Python

|                      |    peewee    | Pony ORM |  SQLAlchemy | Tortoise-ORM |
|----------------------|:------------:|:--------:|:-----------:|:------------:|
| Python version        | 2.7+ or 3.4+ | 2.7 or 3 | 2.7 or 3.6+ |     3.6+     |

## Comparativo: bancos de dados

|                      |    peewee    | Pony ORM |  SQLAlchemy | Tortoise-ORM |
|----------------------|:------------:|:--------:|:-----------:|:------------:|
| SQLite               |       ✔️      |     ✔️    |      ✔️      |       ✔️      |
| MySQL/MariaDB        |       ✔️      |     ✔️    |      ✔️      |       ✔️      |
| PostgreSQL           |       ✔️      |     ✔️    |      ✔️      |       ✔️      |
| Oracle               |              |     ✔️    |      ✔️      |              |
| Microsoft SQL Server |              |          |      ✔️      |              |
| CockroachDB          |       ✔️      |     ✔️    |             |              |

> Em geral, não é recomendado usar o [Django ORM](https://docs.djangoproject.com/en/4.1/ref/databases/) em uma aplicação que não usa o [Django](https://www.djangoproject.com/). Isso se deve ao fato de que o [Django ORM](https://docs.djangoproject.com/en/4.1/ref/databases/) foi projetado especificamente para trabalhar com o framework [Django](https://www.djangoproject.com/) e suas estruturas de dados. Ele pode não funcionar corretamente ou ter limitações em um ambiente fora do [Django](https://www.djangoproject.com/). Se você estiver familiarizado com o [Django ORM](https://docs.djangoproject.com/en/4.1/ref/databases/) e desejar usá-lo em uma aplicação não-Django, é possível fazê-lo. Mas devido a isso, ele não foi incluido nas comparações.

## Qual é o melhor ORM...

Todos os ORMs mencionados são bons e têm suas próprias vantagens e desvantagens. A escolha do melhor ORM para um determinado projeto dependerá das necessidades específicas do projeto e das preferências do desenvolvedor.

Assim, vamos analisar considerando alguns cenários mais específicos e concretos.

### ...para operações rápidas nos dados?

A performance na execução das operações do banco, que costuma ser um dos indicadores mais procurados na hora de escolher um ORM, depende de vários fatores, como: o banco de dados usado, a complexidade das consultas e o tamanho do conjunto de dados.

Portanto, é difícil comparar diretamente a performance de diferentes ORMs sem considerar esses fatores. Mas foi realizado, no início de 2022, um benchmark entre os ORMs considerados diferentes cenários. Uma visão geral do benchmark pode ser visto no gráfico abaixo.

> Disclaimer: o benchmark foi realizado por contribuidores do projeto [Tortoise-ORM](https://tortoise-orm.readthedocs.io/en/latest/)

![Python ORM benchmark](https://tortoise.github.io/_images/ORM_Perf.png)

_Fonte: [https://github.com/tortoise/orm-benchmarks](https://github.com/tortoise/orm-benchmarks)_

### ...para consultas de alta complexidade?

Para um projeto com consultas SQL de alta complexidade, [SQLAlchemy](https://docs.sqlalchemy.org/en/latest/) é uma das melhores opções de ORM para Python. Ele oferece uma grande flexibilidade no que diz respeito a consultas, tanto através do uso de sua API de consultas nativa quanto através do uso de SQL bruto. Além disso, ele permite a utilização de expressões avançadas, sub-consultas e junções complexas, o que é útil para projetos com consultas complexas.

### ...para relacionamentos complexos entre os dados?

Para um projeto com relacionamentos complexos entre as tabelas, [SQLAlchemy](https://docs.sqlalchemy.org/en/latest/) também é uma das melhores opções de ORM para Python. Ele oferece suporte a mapeamento objeto-relacional avançado (ORM) e suporta vários tipos de relacionamentos, incluindo relacionamentos um-para-um, um-para-muitos e muitos-para-muitos. Ele também permite a criação de tabelas de junção e tabelas de associação, o que é útil para lidar com relacionamentos complexos.

Os outros ORMs ([peewee](https://docs.peewee-orm.com/en/latest/), [Pony ORM](https://docs.ponyorm.org/) e [Tortoise-ORM](https://tortoise-orm.readthedocs.io/en/latest/)) também suportam relacionamentos um-para-um, um-para-muitos e muitos-para-muitos, mas a diferença é que o [SQLAlchemy](https://docs.sqlalchemy.org/en/latest/) tem uma abstração mais flexível e completa para trabalhar com relacionamentos complexos. Ele oferece suporte a mapeamento objeto-relacional avançado e recursos avançados de carregamento de dados, como lazy loading e eager loading, que permitem carregar dados relacionados de forma eficiente.

### ...para alta escabilidade?

Para um projeto que exige escalabilidade, [SQLAlchemy](https://docs.sqlalchemy.org/en/latest/) também é uma das melhores opções de ORM para Python. Ele é amplamente utilizado em projetos de produção de grande escala e é uma boa escolha para projetos complexos que possam exigir escalabilidade e performance. Ele tem suporte a vários tipos de engenharia de dados, incluindo recursos de replicação e escalabilidade.

### ...para projetos menores e mais simples

Para um projeto menor e mais simples, que não exige uma grande quantidade de recursos avançados, o [Pony ORM](https://docs.ponyorm.org/) ou [peewee](https://docs.peewee-orm.com/en/latest/) podem ser boas opções. Eles são fáceis de usar e aprender e oferecem uma abstração simples e intuitiva do banco de dados.

### ...com melhor curva de aprendizado

Se você estiver começando a trabalhar com ORMs e deseja algo mais fácil de usar e aprender, o [Pony ORM](https://docs.ponyorm.org/) ou [peewee](https://docs.peewee-orm.com/en/latest/) podem ser boas opções. Eles têm uma curva de aprendizado menor do que o [SQLAlchemy](https://docs.sqlalchemy.org/en/latest/).

### ...para requisitos mínimos de escalabilidade

Para um projeto com requisitos mínimos de escalabilidade, o [Pony ORM](https://docs.ponyorm.org/) ou [peewee](https://docs.peewee-orm.com/en/latest/) também são boas opções. Eles são leves e fáceis de usar e podem ser suficientes para lidar com o tráfego e o volume de dados do seu projeto.

### ...para requisitos específicos de concorrência

Para um projeto com requisitos específicos de concorrência, o [Tortoise-ORM](https://tortoise-orm.readthedocs.io/en/latest/) é uma boa opção pois ele é construído sobre o asyncio e suporta vários bancos de dados.

### ...para requisitos mínimos de segurança

Para um projeto com requisitos mínimos de segurança e não precisar de recursos avançados de segurança, como proteção contra SQL injection, o [Pony ORM](https://docs.ponyorm.org/) ou [peewee](https://docs.peewee-orm.com/en/latest/) podem ser boas opções.

### ...para uso de banco de dados assíncrono

O [Tortoise-ORM](https://tortoise-orm.readthedocs.io/en/latest/) é construído sobre o [asyncio](https://docs.python.org/3/library/asyncio.html) e é uma boa opção para projetos que precisam de alta performance e escalabilidade, especialmente quando se trata de operações assíncronas.

---

## Quais as diferenças entre peewee e Pony ORM?

[Pony ORM](https://docs.ponyorm.org/) e [peewee](https://docs.peewee-orm.com/en/latest/) são muito semelhantes em características, mas existem diferenças:

- [peewee](https://docs.peewee-orm.com/en/latest/) é considerado mais leve e minimalista, enquanto [Pony ORM](https://docs.ponyorm.org/) é mais completo e tem mais recursos.
- [peewee](https://docs.peewee-orm.com/en/latest/) tem uma sintaxe de consulta mais simples e intuitiva, enquanto [Pony ORM](https://docs.ponyorm.org/) tem uma sintaxe mais avançada e poderosa.
- [peewee](https://docs.peewee-orm.com/en/latest/) tem uma documentação mais completa e melhor organizada, enquanto [Pony ORM](https://docs.ponyorm.org/) tem uma documentação menos completa.
- [peewee](https://docs.peewee-orm.com/en/latest/) é mais rápido e tem menos dependências do que [Pony ORM](https://docs.ponyorm.org/).

Em resumo, [peewee](https://docs.peewee-orm.com/en/latest/) é uma boa escolha se você estiver procurando por uma biblioteca simples e fácil de usar, enquanto [Pony ORM](https://docs.ponyorm.org/) é uma boa escolha se você precisar de mais recursos e flexibilidade.

## Por que um ORM assíncrono?

No caso do [Tortoise-ORM](https://tortoise-orm.readthedocs.io/en/latest/) (e agora também, ainda em versão beta, o [asyncio-SQLAlchemy](https://docs.sqlalchemy.org/en/14/orm/extensions/asyncio.html)), é importante entender quando é necessário um ORM assíncrono.

Os bancos de dados atualmente são extremamente rápidos para operações simples, e por isso na maioria das vezes não se faz necessário um ORM assíncrono. O problema que o `asyncio` resolve é quando você precisa lidar com muitas tarefas simultâneas, onde algumas podem inclusive bloquear o I/O (__input/output__, ou escrita/leitura) por tempo arbitrário.

Por exemplo, uma aplicação de bate-papo que atende milhares de usuários simultaneamente. A última coisa que você deseja é ter milhares de threads esperando a resposta de cada um desses usuários, pois isso consumirá desproporcionalmente muitos recursos de hardware. Então, o `asyncio` pode lidar facilmente com essa simultaneidade com uma sobrecarga bem menor.

## Outros ORMs

Vale a pena destacar e conferir outros ORMs, que não foram levados na comparação:

- [SQLObject](http://sqlobject.org/): ORM para fornecer uma interface de objeto para seu banco de dados, com tabelas como classes, linhas como instâncias e colunas como atributos
- [Storm](https://storm.canonical.com/): ORM da Canonical
- [GINO](https://python-gino.org/): um atípico ORM assíncrono
- [Orator](https://orator-orm.com/): ORM com uma implementação `ActiveRecord` simples, mas bonita
- [ormar](https://collerek.github.io/ormar/): mini-ORM assíncrono
- [Prisma Client Python](https://prisma-client-py.readthedocs.io/en/stable/): ORM de próxima geração, construído sobre o [Prisma](https://www.prisma.io/)
- [Piccolo](https://piccolo-orm.com/): ORM e construtor de consultas assíncrono e moderno

---

## Considerações finais

De forma geral, [SQLAlchemy](https://docs.sqlalchemy.org/en/latest/) é o ORM mais maduro e completos para Python, e é amplamente utilizado em projetos de produção de grande escala, ele é flexível e oferece uma grande variedade de recursos. Ele é uma boa escolha para projetos complexos e que possa exigir escalabilidade.

[Pony ORM](https://docs.ponyorm.org/) e [peewee](https://docs.peewee-orm.com/en/latest/) são ORMs mais simples e fáceis de usar, eles são bons para projetos menores e mais simples, e são uma boa escolha para desenvolvedores iniciantes, pois oferecem uma abstração simples e intuitiva do banco de dados.

[Tortoise-ORM](https://tortoise-orm.readthedocs.io/en/latest/) é outro ORM popular para Python, ele é construído sobre o asyncio e suporta SQLite, MySQL e PostgreSQL. Ele é fácil de usar e aprender e é uma boa escolha para projetos com requisitos de concorrência e escalabilidade.

Em resumo, cada ORM tem suas próprias características e recursos únicos, e a escolha do melhor ORM para um projeto dependerá das necessidades específicas do projeto, como a escalabilidade, complexidade das consultas e necessidade de concorrência.

É recomendável experimentar cada uma dessas bibliotecas e comparar as funcionalidades e facilidade antes de tomar uma decisão.