Python Framework

Django

Django é o framework web de Python com a filosofia "batteries-included". Modelos, migrações, um ORM, templates, autenticação e um admin de produção já vêm integrados, permitindo que você descreva seus dados enquanto o Django cuida do resto.

intermediate16 min readUpdated 16 de set. de 2026
blog/views.py
python
# blog/views.py
from django.shortcuts import get_object_or_404, render

from .models import Post


def post_detail(request, slug):
    post = get_object_or_404(
        Post.objects.select_related("author"),
        slug=slug,
        status=Post.Status.PUBLISHED,
    )
    return render(request, "blog/post_detail.html", {"post": post})
Lançado
2005
Linguagem
Python
Padrão
MTV (Model–Template–View)
Banco de Dados
ORM com migrações
Admin
Integrado
Versão atual
5.x

Por que importa

Por que o Django já vem com tudo

Batteries included

O ORM, migrações, templates, formulários, autenticação, sessões e um painel administrativo já vêm inclusos, versionados e testados em conjunto.

Seguro por padrão

Proteção CSRF, hashing de senhas, headers contra clickjacking e queries seguras contra SQL-injection estão ativos, a menos que você os desative deliberadamente.

Um admin real gratuitamente

Registre um model e obtenha uma interface CRUD com busca e filtros para a equipe, transformando a criação de ferramentas internas em um trabalho de poucas horas.

O panorama completo

Model, template, view

Um model detém os dados, um template os renderiza e uma view conecta ambos a uma requisição.

Model

Persistir

Uma classe Python mapeia para uma tabela. Campos descrevem colunas, relações descrevem joins e migrações evoluem o schema.

Template

Renderizar

A linguagem de template do Django mistura HTML com um conjunto deliberadamente pequeno de tags e filtros, com autoescaping ativado por padrão.

View

Manipular

Uma view recebe uma requisição e retorna uma resposta. Ela consulta o ORM, aplica a lógica e escolhe um template ou um código de status.

HTML5 de uma olhada

A caixa de ferramentas do Django

Models e ORM

Defina classes, obtenha tabelas. Faça queries com Python em vez de SQL construído via strings.

URL dispatcher

Mapeie caminhos para views com padrões path() e rotas nomeadas.

Views

Baseadas em funções para clareza, ou baseadas em classes e views genéricas para reuso.

Admin

Um back office gerado automaticamente para cada model que você registrar.

Forms

Formulários declarativos que validam, renderizam e protegem contra adulterações.

Django REST Framework

Serializers, viewsets e routers transformam models em JSON APIs.

Uma breve historia

De uma ferramenta de redação a um pilar da web

  1. 2003

    Nascido em uma redação

    Desenvolvedores do Lawrence Journal-World criam um framework interno para cumprir prazos diários.

    03
  2. 2005

    Lançamento público

    Django torna-se open-source e é nomeado em homenagem ao guitarrista de jazz Django Reinhardt.

    05
  3. 2008

    The Django Software Foundation

    Uma organização sem fins lucrativos assume a gestão, separando o projeto de qualquer empresa individual.

    08
  4. 2014

    Migrações no core

    Migrações de schema tornam-se um recurso nativo, substituindo ferramentas de terceiros.

    14
  5. 2019

    A chegada do Async

    O Django 3.0 adiciona suporte a ASGI, abrindo as portas para views assíncronas e channels.

    19
  6. Hoje

    Um padrão maduro

    O Django 5.x combina um ORM estável com suporte async e fortes type hints.

    Hoje

O guia completo

Django: Tudo que voce precisa saber

O que é Django?

Django é um web framework para Python com a filosofia “batteries-included” (baterias inclusas). Enquanto a maioria dos frameworks exige que você escolha uma camada de banco de dados, um motor de templates, um painel administrativo e um sistema de autenticação, o Django entrega tudo isso em um único pacote coerente. Você descreve seus dados e suas páginas, e o framework cuida das partes tediosas e sensíveis à segurança.

Ele foi construído para a redação de um jornal em 2003 — uma equipe que precisava entregar funcionalidades rapidamente sem sucumbir aos prazos. Essa origem é evidente. O Django privilegia a convenção, a explicitude e a atitude de que “deve haver apenas uma maneira óbvia de fazer as coisas”, mantendo-se como uma das escolhas mais confiáveis para sites com grande volume de conteúdo e orientados a dados.

Se você já passou uma semana inteira conectando um ORM, uma ferramenta de migração e um sistema de autenticação, o Django é o framework que elimina essa semana de trabalho.

O padrão MTV

O Django chama sua arquitetura de MTV: Model, Template, View. É a mesma ideia do MVC, mas com nomes diferentes, e essa diferença costuma confundir as pessoas no início.

  • Um model é uma classe Python que descreve uma tabela e suas linhas.
  • Um template é um arquivo HTML com placeholders que renderiza os dados.
  • Uma view é uma função ou classe que recebe uma requisição, solicita dados aos models e retorna uma resposta.

O controller, no sentido clássico do MVC, é o próprio Django. O dispatcher de URLs decide qual view será executada, e o framework faz a mediação entre as três camadas. Uma vez que esse diagrama esteja claro, todo o restante no Django torna-se um detalhe, e não mais um mistério.

Models e o ORM

Um model é uma classe que herda de models.Model. Cada atributo é um campo, e cada campo se torna uma coluna.

# blog/models.py
from django.conf import settings
from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    body = models.TextField()
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="posts",
    )
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return self.title

CharField precisa de um max_length porque mapeia para um VARCHAR limitado. TextField é ilimitado. auto_now_add=True define um valor apenas na criação, enquanto auto_now=True atualiza a cada salvamento. null controla o banco de dados, e blank controla a validação do formulário; eles são separados propositalmente.

Os relacionamentos são explícitos:

  • ForeignKey é muitos-para-um.
  • ManyToManyField cria uma tabela de junção (join table).
  • OneToOneField é uma chave estrangeira única.

O related_name define o acessor reverso, para que o user.posts.all() funcione do outro lado. O argumento on_delete é obrigatório porque o Django se recusa a adivinhar o que deve acontecer com os filhos quando um pai é deletado.

Migrations

Migrations são arquivos com controle de versão que descrevem as alterações no seu schema. Você escreve o model, e o Django escreve o SQL.

python manage.py makemigrations blog
python manage.py migrate

makemigrations inspeciona seus models e produz uma migration; migrate aplica as migrations pendentes ao banco de dados. Como os arquivos de migration ficam no seu repositório, todos os ambientes terminam com o mesmo schema, e as alterações podem ser revisadas como qualquer outro código. Nunca edite uma migration já aplicada — em vez disso, adicione uma nova.

Querysets e relacionamentos

O ORM retorna querysets, que são lazy (preguiçosos): eles não são executados até que você os itere, fatie ou avalie. Essa característica permite que você encadeie filtros e execute apenas a query final.

Post.objects.filter(status="PB").exclude(author=user).order_by("-published_at")[:10]

Métodos úteis de queryset incluem filter, exclude, get, first, exists, count, values, values_list, annotate e aggregate. As expressões F() permitem que você referencie uma coluna no banco de dados, e objetos Q() permitem que você construa condições OR.

from django.db.models import Count, Q

published = Post.objects.filter(
    Q(status="PB") | Q(published_at__isnull=False)
).annotate(comment_count=Count("comments"))

É aqui que o ORM mostra seu valor: queries complexas permanecem em Python, são parametrizadas de forma segura e são compostíveis.

Evitando o problema N+1

O erro de performance mais comum no Django é a query N+1. Isso acontece quando você acessa um objeto relacionado dentro de um loop, disparando uma query extra para cada linha.

# 1 query for posts, then 1 query per post for the author
for post in Post.objects.all():
    print(post.author.username)

O select_related faz joins de relações de valor único na query original, e o prefetch_related executa uma segunda query e une os resultados no Python para relações de múltiplos valores.

# 1 query for posts joined with author, 1 for the comments
posts = Post.objects.select_related("author").prefetch_related("comments")

Use select_related para foreign keys e campos one-to-one, e prefetch_related para relações many-to-many e relações reversas. Se uma página parecer lenta, conte as queries com o Django Debug Toolbar antes de otimizar qualquer outra coisa.

Views: funções e classes

Uma view é qualquer objeto chamável (callable) que recebe uma request e retorna uma response. Views baseadas em funções são o ponto de partida mais claro.

# blog/views.py
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, render

from .models import Post


def post_list(request):
    posts = Post.objects.filter(status="PB").select_related("author")
    return render(request, "blog/post_list.html", {"posts": posts})


def post_api(request, slug):
    post = get_object_or_404(Post, slug=slug)
    return JsonResponse({"title": post.title, "body": post.body})

Views baseadas em classes trocam um pouco de indireção por reuso. ListView, DetailView, CreateView, UpdateView e DeleteView implementam os padrões CRUD padrão, e mixins permitem que você componha comportamentos.

from django.views.generic import ListView

from .models import Post


class PostListView(ListView):
    model = Post
    template_name = "blog/post_list.html"
    context_object_name = "posts"
    paginate_by = 10

    def get_queryset(self):
        return Post.objects.filter(status="PB").select_related("author")

Use views baseadas em funções para lógicas pontuais e views baseadas em classes genéricas para páginas de listagem e detalhe padrão. Misturar ambas em uma base de código é normal e saudável.

URLs

O dispatcher de URL mapeia um caminho para uma view. Os padrões ficam em urls.py, e cada app pode manter os seus próprios.

# config/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("blog/", include("blog.urls")),
]
# blog/urls.py
from django.urls import path

from . import views

app_name = "blog"

urlpatterns = [
    path("", views.post_list, name="post_list"),
    path("<slug:slug>/", views.post_detail, name="post_detail"),
]

Converters como <int:id> e <slug:slug> validam e tipam o segmento capturado. Sempre nomeie suas rotas e faça a resolução reversa com reverse() ou a tag {% url %} em vez de fixar os caminhos no código (hardcoding), para que os links sobrevivam a uma renomeação.

Templates

Templates são HTML com uma linguagem pequena e intencionalmente limitada. O autoescaping vem ativado por padrão, o que protege você contra XSS.

{% extends "base.html" %}
{% block content %}
  <h1>{{ post.title }}</h1>
  <p>By {{ post.author.username }} on {{ post.published_at|date:"F j, Y" }}</p>
  <div>{{ post.body|linebreaks }}</div>
{% endblock %}

{% extends %} oferece herança de templates, {% include %} compartilha fragmentos, e filtros como date, linebreaks e default formatam valores. Não existe Python arbitrário em templates; se um template precisa de lógica, essa lógica deve pertencer à view ou ao model.

Formulários e validação

O sistema de formulários do Django valida a entrada, renderiza o HTML e protege contra manipulações através de um token CSRF.

# blog/forms.py
from django import forms

from .models import Post


class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ["title", "slug", "body", "status", "published_at"]
        widgets = {"body": forms.Textarea(attrs={"rows": 12})}
# blog/views.py
from django.shortcuts import redirect, render

from .forms import PostForm


def post_create(request):
    if request.method == "POST":
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return redirect(post)
    else:
        form = PostForm()

    return render(request, "blog/post_form.html", {"form": form})

Um ModelForm deriva os campos e a validação a partir do model, garantindo que ambos nunca fiquem dessincronizados. Métodos de limpeza como clean_slug adicionam regras customizadas, e form.is_valid() coleta todos os erros antes de você acessar o banco de dados.

O admin

O admin é o recurso mais subestimado do Django. Registre um model e você terá uma interface pesquisável, filtrável e com controle de permissões para a equipe.

# blog/admin.py
from django.contrib import admin

from .models import Post


@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ["title", "author", "status", "published_at"]
    list_filter = ["status", "created_at"]
    search_fields = ["title", "body"]
    prepopulated_fields = {"slug": ["title"]}
    raw_id_fields = ["author"]
    date_hierarchy = "published_at"

Isso é uma ferramenta interna completa em poucas linhas. Para sites de conteúdo, o admin é frequentemente o próprio produto. Ele também serve como uma barreira de segurança: permissões de staff, has_add_permission e has_change_permission controlam exatamente o que cada função pode fazer.

Configurações e apps

Um projeto Django é uma coleção de apps, onde cada um é um pacote independente com seus próprios models, views, templates e migrations. INSTALLED_APPS os lista, e settings.py armazena a configuração.

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "blog",
]

Mantenha as configurações fora do controle de versão para evitar a exposição de segredos. Leia-as de variáveis de ambiente, divida as configurações por ambiente conforme o projeto cresça e use django-environ ou pydantic-settings para processá-las. Os comandos django-admin startproject e startapp criam a estrutura de ambas as camadas, eliminando qualquer dúvida sobre a organização.

Padrões de segurança

Os padrões do Django foram escolhidos para que a opção mais segura seja também a mais fácil:

  • Tokens CSRF protegem formulários que alteram estados; o middleware rejeita requisições que não os possuam.
  • SQL injection é evitado porque os querysets parametrizam os valores.
  • XSS é mitigado porque os templates escapam variáveis, a menos que você opte por desativar isso com |safe.
  • Senhas são hasheadas com um algoritmo moderno e hashers configuráveis.
  • Clickjacking é bloqueado pelo middleware X-Frame-Options.
  • HTTPS é forçado em produção com SECURE_SSL_REDIRECT, SESSION_COOKIE_SECURE e CSRF_COOKIE_SECURE.

Execute python manage.py check --deploy antes de fazer o deploy. Ele sinaliza as configurações que você esqueceu, e é muito mais barato do que uma revisão de segurança.

Construindo APIs com Django REST Framework

Para APIs JSON, o Django REST Framework (DRF) é o companheiro padrão. Um serializer descreve como um model se torna JSON e vice-versa, enquanto um viewset somado a um router gera os endpoints de CRUD.

# blog/serializers.py
from rest_framework import serializers

from .models import Post


class PostSerializer(serializers.ModelSerializer):
    author = serializers.StringRelatedField()

    class Meta:
        model = Post
        fields = ["id", "title", "slug", "author", "status", "published_at"]
# blog/api.py
from rest_framework import viewsets

from .models import Post
from .serializers import PostSerializer


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.select_related("author")
    serializer_class = PostSerializer
    lookup_field = "slug"
# blog/urls.py
from rest_framework.routers import DefaultRouter

from .api import PostViewSet

router = DefaultRouter()
router.register("posts", PostViewSet, basename="post")

urlpatterns = router.urls

Serializers validam a entrada da mesma forma que os forms fazem, viewsets fornecem as operações de list, create, retrieve, update e delete nativamente, e o DRF pode gerar schemas OpenAPI para a sua API. O ORM do Django e as convenções do DRF são um caminho consolidado para APIs em produção.

Testes

O test runner do Django cria um banco de dados de teste novo e fornece um client que percorre todo o pipeline de requisição.

# blog/tests.py
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

from .models import Post


class PostDetailTests(TestCase):
    def setUp(self):
        user = get_user_model().objects.create_user("ada", password="secret")
        self.post = Post.objects.create(
            title="Hello",
            slug="hello",
            body="First post",
            author=user,
            status=Post.Status.PUBLISHED,
        )

    def test_detail_page_renders(self):
        response = self.client.get(reverse("blog:post_detail", args=["hello"]))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Hello")

    def test_missing_post_returns_404(self):
        response = self.client.get(reverse("blog:post_detail", args=["nope"]))
        self.assertEqual(response.status_code, 404)

TestCase envolve cada teste em uma transação e realiza o rollback, garantindo que os testes permaneçam isolados e rápidos. Use django.test.Client para views, o ORM diretamente para models, e pytest-django se preferir pytest fixtures. Foque em testar o comportamento e as permissões, não os detalhes de implementação.

Melhores práticas

  • Mantenha os models focados em dados e métodos de domínio simples; coloque a orquestração em services ou views.
  • Sempre execute select_related e prefetch_related onde um template utilize relações.
  • Use ModelForm e serializers do DRF para que a validação fique concentrada em um único lugar.
  • Nomeie cada URL e utilize o reverse; nunca escreva caminhos (paths) fixos em templates ou no código.
  • Adicione índices no banco de dados para colunas que você utiliza em filtros e ordenações.
  • Mantenha segredos em variáveis de ambiente e execute check --deploy antes do release.
  • Escreva migrations como artefatos de code review e nunca edite uma migration que já foi aplicada.
  • Teste o pipeline de requisições com Client e a lógica de domínio diretamente.

Erros comuns

  • Iterar sobre um queryset e acessar relações dentro do loop, causando queries N+1.
  • Usar null=True em um campo de string em vez de uma string vazia, e depois ter problemas com verificações de None.
  • Colocar lógica de negócio em templates porque a view parecia muito poluída.
  • Chamar save() dentro de um loop em vez de bulk_create ou bulk_update.
  • Esquecer o on_delete ou escolher CASCADE sem pensar na perda de dados.
  • Deixar DEBUG = True e um SECRET_KEY hardcoded em produção.
  • Tratar o admin como um frontend para o usuário final em vez de uma ferramenta para a equipe.
  • Fazer queries com len(queryset) em vez de .count(), carregando cada linha na memória.

Próximos passos

O Django oferece o caminho completo e convencional para criar um app web em Python pronto para produção. Se você prefere um núcleo mais leve e mais opções de escolha, leia o guia de Flask a seguir. Se estiver construindo uma API JSON assíncrona moderna e quiser que os tipos cuidem da validação, vá para o FastAPI. Independentemente da sua escolha, invista no banco de dados: indexação, transações e planejamento de queries importam mais do que o framework, e o roadmap de backend aprofunda esses conceitos. O guia de REST APIs aborda as regras de design que tornam uma API agradável de consumir.

Na pratica

Quatro arquivos que compõem um app Django

Um model, uma view, um mapa de URLs e um registro de admin formam o ciclo principal de um projeto Django.

blog/models.py
from django.conf import settings
from django.db import models
from django.urls import reverse


class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = "DF", "Draft"
        PUBLISHED = "PB", "Published"

    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    body = models.TextField()
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="posts",
    )
    status = models.CharField(
        max_length=2,
        choices=Status.choices,
        default=Status.DRAFT,
    )
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse("blog:post_detail", kwargs={"slug": self.slug})

Carregando objetos relacionados

Acessar uma foreign key dentro de um loop gera uma query por linha. Busque a relação antecipadamente e a página inteira custará apenas uma query.

Preferir
posts = Post.objects.select_related("author")

for post in posts:
    print(post.author.username)
Evitar
posts = Post.objects.all()

for post in posts:
    # one extra query per iteration (the N+1 problem)
    print(post.author.username)

Lidando com linha inexistente

O get_object_or_404 transforma uma exceção DoesNotExist em uma resposta 404 adequada e mantém a view livre do ruído de try/except.

Preferir
from django.shortcuts import get_object_or_404

post = get_object_or_404(Post, slug=slug)
Evitar
from django.http import Http404

try:
    post = Post.objects.get(slug=slug)
except Post.DoesNotExist:
    raise Http404

Trade-offs

Django é a escolha certa por padrão?

Django otimiza para completude e convenção. Isso é um presente sob prazos apertados e uma restrição quando você deseja algo minimalista.

Strengths

  • Tudo já está lá

    Auth, sessões, formulários, um ORM, migrações e um admin são mantidos por um único projeto, portanto, encaixam-se perfeitamente sem a necessidade de código de integração.

  • Segurança é o padrão

    O framework defende contra o básico da OWASP nativamente, e a maneira documentada de fazer algo geralmente é a maneira segura.

  • Comunidade enorme e estável

    Vinte anos de respostas, pacotes e vagas de emprego significam que quase todo problema já possui uma solução consolidada.

Trade-offs

  • A curva de aprendizado é real

    Settings, apps, o ORM e a linguagem de template são muita coisa para absorver de uma vez. O framework recompensa mais a paciência do que experimentos rápidos.

  • É monolítico por design

    Django assume um banco de dados relacional e uma arquitetura de renderização no servidor ou API. Microserviços e stacks exóticas vão contra a natureza do framework.

  • Suporte Async ainda é jovem

    O ORM ainda é amplamente síncrono, portanto, misturar views async com operações de banco de dados exige cuidado e o uso de threads.

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender Django?

Nosso tutorial interativo te guia por Django passo a passo — com quizzes e codigo real que voce pode executar no navegador.