No ORM do Django, as consultas padrão não suportam passagme de parâmetros diretamente para comparações entre campos e limitam-se a condições lógicas do tipo "e". Para superar essas limitações, utilizamos as classes F e Q.
A classe F permite referenciar valores de campos do banco de dados em consultas, tratando-os como variáveis. A classe Q possibilita a construção de consultas complexas com operadores lógicos como "ou" (|), "e" (&) e "não" (~Q).
Exemplo de Modelo e Dados
from django.db import models
class Produto(models.Model):
id = models.AutoField(primary_key=True)
nome = models.CharField(max_length=50)
preco = models.DecimalField(max_digits=10, decimal_places=2)
comentarios = models.IntegerField(default=0)
visualizacoes = models.IntegerField(default=0)
Utilizando a Classe F
Para comparar campos do banco de dados, usamos F para passar o nome do campo como parâmetro. Isso é útil para consultas condicionais ou atualizações baseadas em outros campos.
from django.db.models import F
# Consulta: produtos onde comentários são maiores que visualizações
resultado_f1 = Produto.objects.filter(comentarios__gt=F('visualizacoes'))
# Atualização: incrementar o número de comentários em 1 para todos os produtos
Produto.objects.update(comentarios=F('comentarios') + 1)
# Renomear campos em anotações
resultado_f2 = Produto.objects.filter(nome__icontains='Django').annotate(
nome_customizado=F('nome')
).values('id', 'nome_customizado')
Utilizando a Classe Q
Para combinar múltiplas condições com operadores lógicos, usamos Q. Isso permite consultas do tipo "ou" ou "não", que não são possíveis com filtros padrão.
from django.db.models import Q
# Condição "e" padrão (sem Q)
resultado_e = Produto.objects.filter(preco__lt=100, comentarios__gt=5)
# Condição "ou" com Q
resultado_ou = Produto.objects.filter(Q(preco__lt=100) | Q(comentarios__gt=5))
# Condição "não" com ~Q
resultado_nao = Produto.objects.filter(Q(preco__lt=100) & ~Q(visualizacoes__gt=50))
# Combinação complexa
resultado_complexo = Produto.objects.filter(
Q(preco__lt=100) | (~Q(comentarios__gt=20) & Q(nome__startswith='Py'))
)
Uso Avançado de Q
Q permite construir consultas dinamicamente, por exemplo, com base em parâmetros de entrada.
def buscar_produtos(request):
preco_maximo = request.GET.get('preco')
nome_filtro = request.GET.get('nome')
consulta = Q()
if preco_maximo:
consulta &= Q(preco__lte=float(preco_maximo))
if nome_filtro:
consulta &= Q(nome__icontains=nome_filtro)
return Produto.objects.filter(consulta)
SQL Nativo no Django
Para situações que exigem SQL puro, o Django oferece duas abordagens principais. É importante usar parâmetros para evitar injeção de SQL.
1. Usando o Método raw()
resultado_raw = Produto.objects.raw('SELECT * FROM app_produto WHERE preco > %s', [50.0])
for item in resultado_raw:
print(item.nome, item.preco)
2. Usando o Módulo connections
from django.db import connections
with connections['default'].cursor() as cursor:
cursor.execute('UPDATE app_produto SET preco = preco * 1.1 WHERE id = %s', [1])
cursor.execute('SELECT nome, preco FROM app_produto WHERE visualizacoes > %s', [100])
linhas = cursor.fetchall() # fetchone(), fetchmany(n)
Transações no Django
Transações garantem a atomicidade de operações, essenciais para manter a consistência dos dados.
Craacterísticas ACID
- Atomicidade: Todas as operações de uma transação são executadas ou nenhuma é.
- Consistência: O banco de dados permanece em um estado válido após a transação.
- Isolamento: Transações concorrentes não interferem entre si.
- Durabilidade: Alterações confirmadas são permanentes.
Uso Básico de Transações
from django.db import transaction
# Transação em um bloco de contexto
try:
with transaction.atomic():
Produto.objects.create(nome='Item A', preco=10.0)
Produto.objects.create(nome='Item B', preco='texto_inválido') # Isto causa um erro
except Exception:
print('Transação revertida devido a erro.')
Locks e Concorrência
O Django suporta locks para controle de concorrência, como o select_for_update, que bloqueia registros durante uma transação.
from django.db import transaction
with transaction.atomic():
produto = Produto.objects.select_for_update().get(id=1)
produto.visualizacoes += 1
produto.save()
Configurações Avançadas de Transações
Para habilitar transações globalmente nas configurações do Django:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'meubanco',
'ATOMIC_REQUESTS': True # Habilita transações automaticamente para cada requisição
}
}
Para desativar transações em uma view específica, use o decorador non_atomic_requests:
from django.db import transaction
from django.utils.decorators import method_decorator
from rest_framework.views import APIView
@method_decorator(transaction.non_atomic_requests, name='dispatch')
class MinhaView(APIView):
def get(self, request):
# Operações que não precisam de transação
return Response({'status': 'ok'})