Pedro Fonseca
Omnes enim Christus, nihil sine Maria

18 de setembro de 2025

Última atualização em 21 de setembro de 2025

select_related e prefetch_related no Django

Cenário: Modelos da NFL

Imagine que temos os seguintes modelos:

from django.db import models

class Conference(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name


class Team(models.Model):
    name = models.CharField(max_length=100)
    city = models.CharField(max_length=100)
    conference = models.ForeignKey(Conference, on_delete=models.CASCADE)

    def __str__(self):
        return f"{self.city} {self.name}"


class Player(models.Model):
    name = models.CharField(max_length=100)
    position = models.CharField(max_length=10)
    team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name="players")

    def __str__(self):
        return self.name

Problema: Consultas N+1

Ao buscar jogadores e suas equipes, podemos enfrentar o problema de consultas N+1:

players = Player.objects.all()


print(players.query)
"""
SELECT "player"."id",
       "player"."name",
       "player"."position",
       "player"."team_id"
FROM "player";
"""

for player in players:
    print(f"{player.name} - {player.team.name}")

Repare que eu tento acessar player.team.name dentro do loop.

Isso resulta em uma consulta para buscar todos os jogadores e, em seguida, uma consulta adicional para cada jogador para buscar sua equipe.

Qual é o real problema e por que chamamos de consulta N+1?

Ou seja,


Usando select_related

select_related é usado para relações ForeignKey e OneToOneField. Ele realiza um JOIN SQL para buscar os dados relacionados em uma única consulta.

players = Player.objects.select_related('team').all()

resultando em:

SELECT
    "player"."id",
    "player"."name",
    "player"."position",
    "player"."team_id",
    "team"."id",
    "team"."name",
    "team"."city",
    "team"."conference_id"
FROM "player"
INNER JOIN "team" ON "player"."team_id" = "team"."id";

Não é bala de prata

Em vez de N+1 consultas, agora temos apenas 1 consulta.

Apesar de realizar uma única consulta, é importante adicionar que dependendo do número de colunas e do tamanho dos dados, a consulta pode se tornar mais pesada, e consequentemente consumindo mais memória. Como isso pode acontecer?

O Django trará os 1000 jogadores e todos os logos dos times de uma vez. Se cada logo tiver 50KB, isso resultará em 50MB de dados só para os logos. Isso pode ser um problema de performance e consumo de memória.