O ORM do Django é simples e intuitivo no seu uso mas muitos devs acreditam que consultas SQL mais avançadas são demais para essa ferramenta.
A verdade é que o ORM do Django vai muito além do consultas básicas, e nesta publicação vou mostrar como usar recursos poderosos deste ORM para resolver situações reais de forma limpa e performática.
Expressões
Um dev iniciante com o ORM, provavelmente faria uma operação de atualizar um campo dessa seguinte maneira, o que não só requer mais etapas, além de que esse fluxo pode ser problemático, especialmente em situações de concorrência, porque o valor pode mudar entre o SELECT e o UPDATE.
produto = Produto.objects.get(id=1) # Isso faz um SELECT no banco
produto.estoque -= 1 # Operação feita em Python
produto.save() # Isso faz um UPDATE com o novo valor
A classe F
permite referenciar valores de outros campos da mesma linha diretamente no banco, sem precisar carregar os dados pro Python.Com F
, a operação ocorre direto no banco de dados de forma segura:
from django.db.models import F
Produto.objects.filter(id=1).update(
estoque=F('estoque') - 1
)
Essa operação é atômica, ou seja, evita condições de corrida entre processos concorrentes. O Django gera diretamente um UPDATE estoque = estoque - 1
no banco, sem realizar um SELECT
antes. Sendo esse o código equivalente em SQL.
UPDATE produto
SET estoque = estoque - 1
WHERE id = 1;
Subconsultas
Como já estamos acostumados a fazer utlizando SQL,Subqueries são ideais quando você precisa anexar dados derivados de outras tabelas. Vejamos essa consulta feita em SQL puro que anexa o último pedido de um cliente a consulta.
SELECT *,
(SELECT data
FROM pedidos
WHERE pedidos.cliente_id = clientes.id
ORDER BY data DESC
LIMIT 1) AS data_ultimo_pedido
FROM clientes;
A primeiro momento parece bem chato fazer essa subquery usando somente o ORM, mas vamos ver algumas classes que irão nos ajudar com isso. Subquery
e OuterRef
são usados juntos para inserir subconsultas SQL dentro de uma consulta principal. O Subquery
permite definir uma consulta que será executada para cada linha da consulta externa. Já o OuterRef
é usado dentro da subconsulta para referenciar dinamicamente uma coluna da consulta externa (como o ID do cliente no nosso caso). Essa mesma consulta agora feita somente com o Django ORM ficaria assim:
from django.db.models import OuterRef, Subquery
from pedidos.models import Pedido
from clientes.models import Cliente
ultima_data = Pedido.objects.filter(
cliente=OuterRef('pk')
).order_by('-data').values('data')[:1]
clientes = Cliente.objects.annotate(
data_ultimo_pedido=Subquery(ultima_data)
)
# ou podemos fazer isso tudo inline
clientes = Cliente.objects.annotate(
data_ultimo_pedido=Subquery(
Pedido.objects.filter(
cliente=OuterRef('pk')
).order_by('-data').values('data')[:1]
)
)
NOTA: Sempre use.values('campo')[:1]
nas Subquery()
para garantir que a subconsulta retorne exatamente um valor escalar, evitando erros e tornando a consulta válida para ser usada em annotate()
.
Window functions
Em SQL, window functions (ou funções de janela) são funções que realizam cálculos em um conjunto de linhas relacionando à linha atual sem colapsar o resultado como fazem as funções de agregação tradicionais (SUM
, AVG
, COUNT
, etc). Queries com window functions são úteis para análises que dependem de agrupamentos e ordenações, e o Django já suporta isso de forma nativa.
# ranquear os produtos mais vendidos por categoria
from django.db.models import Window, F
from django.db.models.functions import RowNumber
from produtos.models import Produto
produtos = Produto.objects.annotate(
rank=Window(
expression=RowNumber(),
partition_by=[F('categoria')],
order_by=F('vendas').desc()
)
)
--Equivalente em SQL puro
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY categoria_id
ORDER BY vendas DESC
) AS rank
FROM produtos_produto;
Conclusão
O Django ORM vai muito além do que parece. Ferramentas como as vistas nesta publicação permitem expressar consultas complexas com clareza e segurança, direto do Python. Em vez de fugir pro SQL cru de cara, tente explorar esses recursos. Muitas vezes, a solução está ao seu alcance e pode deixar o seu código bem mais limpo do que você imagina. Existem ainda mais funções complexas do ORM a serem exploradas, quem sabe não exploro elas numa próxima publicação, até a próxima.
Referências
Você pode dar uma olhada com mais calma nessa e outras várias funções avançadas de consulta, diretamente na documentação do Django:
Top comments (1)
Explicação super clara, abriu uma nova perspectiva. Muito bom!