O Ciclo de Requisição MTV do Django e a Preguiça do ORM
O Django chama sua própria arquitetura de MTV: Model, Template, View.
Esse nome causa mais confusão do que resolve, porque o "View" do Django é a camada de controle no sentido clássico do MVC, e o próprio Django - o resolvedor de URL e os internos do framework - desempenha o papel que o MVC atribui à "view".
Por baixo dessa peculiaridade de nomenclatura, existe uma arquitetura real: cada requisição passa por uma pilha aninhada de middleware, é roteada para uma função ou classe de view, e essa view tipicamente conversa com modelos cujos querysets são preguiçosos por design.
Esta página é o modelo mental por trás de ambas as partes: como uma requisição realmente se move através do Django e por que o ORM se comporta da maneira que se comporta assim que uma view começa a consultá-lo.
O Django Basics cobre a configuração de projetos e aplicativos na prática; Models & the ORM e Migrations aprofundam a camada de dados que esta página introduz conceitualmente.
Resumo
- Uma requisição Django flui através de uma pilha de middleware aninhada até uma view, que coordena consultas de modelo e templates avaliados preguiçosamente, e a resposta flui de volta através do mesmo middleware em ordem inversa.
- Por que Importa: A maioria dos bugs de performance do Django (consultas N+1) e a maioria dos bugs de middleware (ordem incorreta) vêm diretamente do mal-entendido dessas duas mecânicas, não do próprio Django ser imprevisível.
- Conceitos-Chave: o padrão MTV, a cebola de middleware, o resolvedor de URL, a preguiça do QuerySet, migrações como estado de esquema rastreado.
- Quando Usar Este Modelo: Ao decidir onde o middleware deve ficar em
MIDDLEWARE, depurando por que um queryset executou mais SQL do que o esperado, ou explicando por quemakemigrationsemigratesão duas etapas separadas. - Limitações / Trade-offs: A preguiça do ORM e a cebola de middleware trocam explicitude por conveniência - elas tornam o código simples fácil, mas escondem exatamente quando uma viagem de ida e volta ao banco de dados ou uma mutação de resposta realmente acontece.
- Tópicos Relacionados: o padrão MVC clássico, a camada de serialização do Django REST Framework, o despacho de requisições WSGI/ASGI, o planejamento de consultas SQL.
Fundamentos
A nomenclatura MTV do Django é uma fonte de confusão quase permanente para quem vem de frameworks MVC: no Django, o Model é a mesma ideia do model do MVC, mas o Template do Django é a "view" do MVC (a camada de renderização), e a View do Django é o "controller" do MVC (a lógica que decide o que renderizar).
O próprio Django - especificamente o resolvedor de URL - ocupa o papel que o MVC chama de "view" no sentido de coordenar tudo, razão pela qual alguma documentação chama a abordagem do Django de padrão MTV ou "MVT" em vez de afirmar uma equivalência MVC direta.
A jornada de uma requisição começa no ponto de entrada WSGI (ou ASGI, em implantações com capacidade assíncrona), passa por todo o middleware configurado em ordem, é comparada com os padrões de urls.py para encontrar uma view, e o valor de retorno da view viaja de volta pela mesma pilha de middleware em ordem inversa.
O Middleware no Django não é uma cadeia plana como a do Express; é uma cebola aninhada: cada middleware envolve o próximo, de modo que um middleware pode executar código tanto antes da view ser executada (enquanto a requisição entra) quanto depois que ela retorna (enquanto a resposta sai).
Requisição -> [SecurityMiddleware -> [SessionMiddleware -> [CommonMiddleware -> View] ] ]
Resposta <- [SecurityMiddleware <- [SessionMiddleware <- [CommonMiddleware <- View] ] ]Esse aninhamento é o motivo pelo qual a ordem na lista MIDDLEWARE importa em ambas as direções: um middleware próximo ao topo da lista é executado primeiro na entrada, mas por último na saída, envolvendo tudo abaixo dele.
Mecânicas e Interações
Assim que uma view começa a trabalhar com models, o comportamento que mais surpreende os novatos é que um QuerySet não executa nenhum SQL quando é definido.
Article.objects.filter(status="live") constrói uma descrição de consulta, não um resultado; o SELECT real só dispara quando algo força a avaliação - iterá-lo, fatiá-lo de uma forma que não seja um filtro adicional, chamar list() nele, verificar sua veracidade, ou acessar len().
Essa preguiça é o que faz com que chamadas encadeadas como .filter().exclude().order_by() funcionem sem três viagens de ida e volta: cada chamada retorna um novo QuerySet ainda não avaliado, e apenas o ponto de avaliação final toca o banco de dados.
A preguiça tem um custo bem conhecido, no entanto: acessar um campo de um objeto relacionado dentro de um loop (book.author.name para cada book em um queryset) dispara uma consulta por iteração, porque o objeto relacionado é ele próprio buscado preguiçosamente na primeira vez que é tocado - este é o clássico problema de consulta N+1, e ele existe especificamente porque a preguiça torna fácil escrever código que parece acesso a atributo simples, mas que emite silenciosamente uma consulta.
select_related (para ForeignKey/OneToOne, via um JOIN SQL) e prefetch_related (para ManyToMany/ForeignKey reverso, via uma segunda consulta e junção em Python) existem para colapsar esse padrão N+1 em uma ou duas consultas, mas eles precisam ser solicitados explicitamente - o Django não infere o padrão de acesso com antecedência.
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=120)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
# Uma consulta total, não uma consulta por livro:
Book.objects.select_related("author").filter(author__name__startswith="A")As Migrações seguem um modelo relacionado, mas distinto: o Django rastreia o estado de cada modelo a partir da última migração, e makemigrations gera um novo arquivo de migração comparando seu código de modelo atual com esse estado rastreado - ele não lê o esquema do banco de dados ativo para decidir o que mudou.
migrate então aplica os arquivos de migração pendentes no banco de dados real, na ordem de dependência registrada dentro de cada migração, que é por que as migrações são arquivos commitados no controle de versão em vez de uma etapa de sincronização automática em tempo de execução.
Considerações Avançadas e Aplicações
O ORM do Django foi projetado inicialmente para ser síncrono, e essa suposição ainda molda as views assíncronas: o Django adicionou suporte a views async def e implantação ASGI, mas os métodos de consulta do ORM permanecem em grande parte síncronos, de modo que uma view assíncrona que consulta o banco de dados precisa de ponte sync_to_async (ou um dos métodos ORM assíncronos mais recentes adicionados em lançamentos recentes) em vez de obter I/O assíncrono nativo gratuitamente.
Isso significa que "minha view é async def" não significa automaticamente "meu acesso ao banco de dados não é bloqueante" - veja Async Views & Channels para onde essa fronteira realmente se situa.
O site de administração é um benefício direto da introspecção da camada de modelo: admin.site.register(Model) funciona porque o Django pode ler os campos, relacionamentos e validadores de um modelo para gerar formulários CRUD sem nenhum código específico de administração, que é também por que o admin não é uma boa opção como interface pública - ele expõe exatamente a forma dos seus modelos, não uma interface curada.
O cache interage com ambas as metades deste modelo: o cache em nível de middleware (UpdateCacheMiddleware/FetchFromCacheMiddleware) envolve respostas inteiras usando a mesma mecânica de cebola descrita acima, enquanto o cache em nível de queryset se aplica apenas dentro do cache de resultados de um único QuerySet avaliado, não entre requisições - veja Caching & Performance.
| Abordagem | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| MTV + ORM Preguiçoso do Django | Inclui tudo (admin, auth, ORM, forms); convenções consistentes em uma base de código grande | ORM síncrono mesmo sob views assíncronas; a preguiça esconde custos N+1 até a profilaxia | Aplicativos focados em conteúdo ou CRUD que se beneficiam de uma estrutura compartilhada e opinativa |
| FastAPI + um ORM assíncrono independente | I/O assíncrono nativo em toda parte; limite explícito e tipado de requisição/resposta | Sem admin, auth ou camada de template integrados - montados a partir de pacotes separados | Serviços com foco em API onde a concorrência assíncrona e a validação de esquema são mais importantes |
| Flask + Flask-SQLAlchemy | Mínimo, adicione apenas o necessário | Menos estrutura por padrão; os padrões de preguiça do ORM ainda se aplicam, com menos ferramentas para pegá-los | Pequenos serviços ou equipes que desejam controle granular sobre a pilha |
Equívocos Comuns
- "MTV é apenas o nome do Django para MVC." O mapeamento não é um para um: o "Template" do Django desempenha o papel que o MVC chama de "view" (renderização), e a "View" do Django desempenha o papel que o MVC chama de "controller" (lógica de tratamento de requisições), o que é o oposto do que a palavra compartilhada "view" sugere.
- "Um QuerySet executa sua consulta assim que você a escreve." Ele não executa nenhum SQL até que algo force a avaliação - iteração,
list(), fatiamento sem filtro adicional, ou uma verificação booleana/de comprimento - que é o que torna possível o encadeamento seguro de métodos em primeiro lugar. - "Migrações mantêm o banco de dados sincronizado automaticamente."
makemigrationsapenas gera arquivos de migração comparando seu código de modelo atual com o estado do modelo rastreado pelo Django; alguém (ou uma etapa de implantação) ainda precisa executarmigratepara que o banco de dados realmente mude. - "A ordem do middleware em
MIDDLEWAREmal importa." Como o middleware se aninha como uma cebola, as mudanças de ordem afetam tanto quando a lógica é executada na entrada quanto quando é executada na saída - middleware relacionado à segurança e sessão, em particular, dependem de ordenação estrita. - "Views assíncronas tornam o acesso ao banco de dados do Django assíncrono." Funções de view podem ser
async def, mas a maior parte da execução de consultas do ORM ainda é síncrona nos bastidores e precisa de ponte explícita para ser executada com segurança dentro de uma view assíncrona.
FAQs
O que significa MTV e como difere de MVC?
Model-Template-View. O Template do Django desempenha o papel que o MVC chama de "view" (renderização), e a View do Django desempenha o papel que o MVC chama de "controller" (lógica de tratamento de requisições) - a palavra compartilhada "view" se refere a camadas diferentes em cada padrão.
Um QuerySet do Django acessa o banco de dados quando é criado?
Não. Ele constrói uma descrição de consulta preguiçosa; o SQL é executado apenas quando o QuerySet é avaliado por iteração, fatiamento (de forma não filtrante), list(), ou uma verificação de veracidade/comprimento.
Por que encadear `.filter().exclude().order_by()` não executa três consultas separadas?
Cada chamada retorna um novo QuerySet ainda não avaliado, construído a partir do anterior; apenas o ponto em que algo realmente força a avaliação dispara uma única consulta SQL combinada.
O que é o problema de consulta N+1, concretamente, no Django?
Iterar sobre um queryset e acessar um campo relacionado (como book.author.name) dispara uma consulta adicional por iteração, porque o objeto relacionado é buscado preguiçosamente na primeira vez que é acessado - select_related/prefetch_related colapsam isso em uma ou duas consultas.
Como escolho entre select_related e prefetch_related?
select_relatedparaForeignKey/OneToOne- usa umJOINSQL, portanto, funciona apenas para relações de valor únicoprefetch_relatedparaManyToManyouForeignKeyreverso - executa uma segunda consulta e une os resultados em Python
Executar `makemigrations` altera meu banco de dados?
Não. Ele apenas gera um arquivo de migração comparando seu código de modelo atual com o estado do modelo rastreado pelo Django. migrate é a etapa separada que realmente aplica as alterações ao banco de dados.
Por que a ordem do middleware em settings.py importa tanto?
Porque o middleware se aninha como uma cebola: cada entrada envolve tudo abaixo dela, executando código antes da view na ordem da lista e código após a view na ordem inversa, então mover um middleware muda tanto quando ele vê a requisição quanto quando ele vê a resposta.
Como o admin do Django gera formulários sem código específico de admin?
Ele introspecta os campos, tipos e relacionamentos de cada modelo registrado em tempo de execução para construir visualizações de lista e formulários CRUD automaticamente - que é também por que a UI do admin espelha diretamente a forma do seu modelo em vez de um design curado.
As views assíncronas do Django são totalmente assíncronas, incluindo o banco de dados?
Não por padrão. Funções de view podem ser declaradas async def, mas a maior parte da execução de consultas do ORM permanece síncrona nos bastidores e precisa de ponte explícita sync-to-async para ser executada com segurança dentro de uma view assíncrona.
Onde o cache de resultados do queryset se aplica?
Apenas dentro de uma única instância de QuerySet avaliada - uma vez avaliado, iterá-lo novamente reutiliza o conjunto de resultados em cache, mas um novo QuerySet construído a partir de um .filter() fresco não compartilha esse cache e emite sua própria consulta.
Por que é chamado de "MTV" em vez de "MVC" se o Django é claramente inspirado pelo MVC?
A própria documentação do Django adotou MTV especificamente para evitar implicar uma equivalência MVC direta, já que o próprio Django (o framework e o despacho de URL) preenche o papel que o MVC atribui à "view", o que de outra forma seria uma sobreposição de terminologia confusa.
A preguiça significa que os querysets são sempre seguros para passar e reutilizar?
Eles são seguros para construir e compor, mas cada nova chamada de filter/exclude/order_by retorna um objeto QuerySet distinto; reutilizar os resultados em cache de um QuerySet já avaliado para diferentes necessidades de filtragem não refletirá mudanças encadeadas adicionais.
Relacionados
- Django Basics - projetos, apps, URLs e views na prática
- Models & the ORM - campos, relacionamentos e padrões de queryset em profundidade
- Migrations - como os arquivos de migração são gerados e aplicados
- Caching & Performance - cache em nível de middleware e em nível de queryset
- Async Views & Channels - a fronteira ORM assíncrona/síncrona na prática
- Django REST Framework - uma camada de serialização construída sobre este mesmo ciclo de requisição
Versões da Stack: Esta página foi escrita para Django 5.2 em Python 3.14 (manutenção: 3.13).