Guia Completo de Django 2025: Do Básico ao Avançado

Cansado de programar?

Cansado(a) de quebrar a cabeça para aprender a programar Python de verdade?

Conheça a melhor e mais completa formação de Python e Django e sinta-se um programador verdadeiramente competente. Além de Python e Django, você também vai aprender Banco de Dados, SQL, HTML, CSS, Javascript, Bootstrap e muito mais!

Quero aprender Python e Django de Verdade! Quero aprender!
Suporte

Tire suas dúvidas diretamente com o professor

Suporte

Projetos práticos voltados para o mercado de trabalho

Suporte

Formação moderna com foco na prática profissional

Download do Artigo

Salve salve Pythonista!

Django se consolidou como um dos frameworks web mais poderosos e completos do ecossistema Python.

Em 2025, com a versão 5.1 LTS (Long Term Support), o Django continua evoluindo, adicionando suporte robusto para operações assíncronas, WebSockets, APIs RESTful e mantendo sua filosofia de “baterias incluídas”.

Este é o guia definitivo de Django em 2025: um artigo pilar que reúne tudo o que você precisa saber para dominar o framework, desde a instalação até o desenvolvimento de aplicações web complexas e APIs profissionais.

Se você está começando agora ou quer aprofundar seus conhecimentos, este guia é para você! :rocket:

Vá Direto ao Assunto…

O que é Django e por que usar em 2025?

Django é um framework web de alto nível escrito em Python que incentiva o desenvolvimento rápido e o design limpo e pragmático.

Criado em 2005, Django amadureceu ao longo de duas décadas e hoje é a escolha de empresas como Instagram, Spotify, Mozilla, Pinterest e NASA.

Por que Django continua relevante em 2025?

1. Baterias Incluídas Django vem com tudo que você precisa out-of-the-box:

  • ORM poderoso para banco de dados
  • Sistema de autenticação completo
  • Painel administrativo automático
  • Proteção contra vulnerabilidades (SQL Injection, XSS, CSRF)
  • Sistema de templates robusto
  • Gerenciamento de arquivos estáticos e media

2. Suporte Async Completo (Django 5.1)

  • Views assíncronas (async def)
  • Middlewares assíncronos
  • Suporte ASGI nativo
  • WebSockets integrados
  • Melhor performance em operações I/O

3. Ecossistema Rico

  • Django REST Framework para APIs
  • Django Channels para real-time
  • Celery para tarefas assíncronas
  • Integração perfeita com PostgreSQL, MySQL, Oracle

4. Comunidade Ativa

  • LTS (Long Term Support) até 2026
  • Atualizações regulares de segurança
  • Documentação excepcional
  • Comunidade global gigantesca

5. Produtividade O lema “Don’t Repeat Yourself” (DRY) do Django significa que você escreve menos código e entrega mais rápido.

Instalação e Configuração do Django 5.1

Pré-requisitos

Antes de começar, certifique-se de ter:

  • Python 3.10+ instalado
  • pip (gerenciador de pacotes Python)
  • Ambiente virtual configurado

Criando um Ambiente Virtual

É fundamental usar ambientes virtuais para isolar dependências do projeto:

1
2
3
4
5
6
7
8
# Criar ambiente virtual
python -m venv venv

# Ativar no Linux/Mac
source venv/bin/activate

# Ativar no Windows
venv\Scripts\activate

Para entender mais sobre ambientes virtuais, leia nosso guia completo sobre Virtualenv.

Instalando Django 5.1

Com o ambiente virtual ativado, instale a versão mais recente:

1
2
3
4
pip install django

# Verificar versão instalada
python -m django --version

Versão atual (2025): Django 5.1 LTS com suporte estendido até abril de 2026.

Criando Seu Primeiro Projeto

Django fornece o utilitário django-admin para iniciar projetos:

1
2
django-admin startproject meu_projeto
cd meu_projeto

Isso cria a seguinte estrutura:

1
2
3
4
5
6
7
8
meu_projeto/
    manage.py
    meu_projeto/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py

Arquivos importantes:

  • manage.py - Interface de linha de comando do projeto
  • settings.py - Configurações do projeto
  • urls.py - Roteamento de URLs
  • asgi.py - Configuração ASGI (async)
  • wsgi.py - Configuração WSGI (tradicional)

Django 5.1: ASGI é o padrão recomendado para novos projetos, oferecendo suporte completo a operações assíncronas.

Para um tutorial passo a passo, confira: Seu primeiro projeto Django em 15 minutos.

Executando o Servidor de Desenvolvimento

1
python manage.py runserver

Acesse http://127.0.0.1:8000/ e veja a página de boas-vindas do Django! :rocket:

Arquitetura MTV: Model-Template-View

Django segue o padrão MTV (Model-Template-View), uma variação do MVC (Model-View-Controller):

Model (Modelo)

Representa a camada de dados - a estrutura do banco de dados e a lógica de negócios.

Template (Template)

Representa a camada de apresentação - como os dados são exibidos ao usuário (HTML).

View (Visão)

Representa a camada de controle - processa requisições HTTP e retorna respostas.

Fluxo de uma requisição Django:

1
2
3
4
5
6
1. Usuário acessa URL → urls.py
2. URL mapeia para View → views.py
3. View consulta Model → models.py
4. Model retorna dados do banco de dados
5. View processa dados e renderiza Template → templates/*.html
6. Template é retornado como resposta HTTP ao usuário

Para entender profundamente a arquitetura, leia: Django: Introdução ao framework.

:rocket: Quer criar Ebooks técnicos com syntax highlighting perfeito, geração de conteúdo via IA e exportação em PDF profissional? Então clica no botão abaixo e conheça já o DevBook.ai!

Models: A Camada de Dados do Django

Models definem a estrutura dos seus dados e são mapeados automaticamente para tabelas no banco de dados através do poderoso ORM do Django.

Definindo um Model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# models.py
from django.db import models

class Post(models.Model):
    titulo = models.CharField(max_length=200)
    conteudo = models.TextField()
    autor = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    data_publicacao = models.DateTimeField(auto_now_add=True)
    data_atualizacao = models.DateTimeField(auto_now=True)
    publicado = models.BooleanField(default=False)
    
    class Meta:
        ordering = ['-data_publicacao']
        verbose_name_plural = 'Posts'
    
    def __str__(self):
        return self.titulo

Tipos de Campos Mais Usados

  • CharField - Texto curto (max_length obrigatório)
  • TextField - Texto longo
  • IntegerField - Números inteiros
  • DecimalField - Números decimais precisos
  • DateTimeField - Data e hora
  • BooleanField - Verdadeiro/Falso
  • ForeignKey - Relacionamento um-para-muitos
  • ManyToManyField - Relacionamento muitos-para-muitos
  • EmailField - E-mail com validação
  • URLField - URL com validação
  • ImageField - Upload de imagens

Migrations: Gerenciando o Esquema do Banco

Django usa migrations para gerenciar alterações no banco de dados:

1
2
3
4
5
6
7
8
9
10
11
# Criar migrations baseadas em mudanças nos models
python manage.py makemigrations

# Aplicar migrations ao banco de dados
python manage.py migrate

# Ver SQL que será executado (sem executar)
python manage.py sqlmigrate app_name 0001

# Verificar problemas sem executar
python manage.py makemigrations --check

Dica Pro: Use --check em pipelines de CI/CD para detectar models não migrados.

Artigos relacionados:

Django ORM: Consultando Dados

O ORM do Django permite consultas complexas sem escrever SQL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# Buscar todos os posts
Post.objects.all()

# Filtrar posts publicados
Post.objects.filter(publicado=True)

# Buscar um post específico
Post.objects.get(id=1)

# Excluir posts não publicados
Post.objects.exclude(publicado=True)

# Ordenar por data
Post.objects.order_by('-data_publicacao')

# Limitar resultados
Post.objects.all()[:5]

# Busca com múltiplos filtros
Post.objects.filter(
    publicado=True,
    data_publicacao__year=2025
)

# Relacionamentos (joins automáticos)
Post.objects.select_related('autor').all()

# Agregações
from django.db.models import Count
Post.objects.aggregate(total=Count('id'))

Métodos de QuerySet essenciais:

  • filter() - Filtrar registros
  • exclude() - Excluir registros
  • get() - Buscar um único registro
  • first() / last() - Primeiro/último registro
  • exists() - Verificar se existe
  • count() - Contar registros
  • select_related() - Otimizar ForeignKey (JOIN)
  • prefetch_related() - Otimizar ManyToMany

Views: Processando Requisições

Views são funções ou classes que recebem requisições HTTP e retornam respostas.

Function-Based Views (FBV)

1
2
3
4
5
6
7
8
9
10
11
# views.py
from django.shortcuts import render, get_object_or_404
from .models import Post

def lista_posts(request):
    posts = Post.objects.filter(publicado=True)
    return render(request, 'blog/lista.html', {'posts': posts})

def detalhe_post(request, post_id):
    post = get_object_or_404(Post, id=post_id)
    return render(request, 'blog/detalhe.html', {'post': post})

Class-Based Views (CBV)

Django oferece views genéricas que economizam muito código:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# views.py
from django.views.generic import ListView, DetailView, CreateView
from .models import Post

class PostListView(ListView):
    model = Post
    template_name = 'blog/lista.html'
    context_object_name = 'posts'
    paginate_by = 10
    
    def get_queryset(self):
        return Post.objects.filter(publicado=True)

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/detalhe.html'
    context_object_name = 'post'

class PostCreateView(CreateView):
    model = Post
    fields = ['titulo', 'conteudo']
    template_name = 'blog/criar.html'
    success_url = '/posts/'

CBVs genéricas mais usadas:

  • ListView - Listar registros
  • DetailView - Exibir um registro
  • CreateView - Criar registro
  • UpdateView - Atualizar registro
  • DeleteView - Deletar registro
  • TemplateView - Renderizar template simples
  • FormView - Processar formulários

Django 5.1: CBVs agora suportam métodos assíncronos (async def get(), async def post()).

Artigos relacionados:

Roteamento de URLs

Configure URLs para mapear para views:

1
2
3
4
5
6
7
8
9
# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('posts/', views.PostListView.as_view(), name='lista_posts'),
    path('posts/<int:pk>/', views.PostDetailView.as_view(), name='detalhe_post'),
    path('posts/criar/', views.PostCreateView.as_view(), name='criar_post'),
]

Django 5.1 usa path() como padrão. Para expressões regulares complexas, use re_path():

1
2
3
4
5
from django.urls import re_path

urlpatterns = [
    re_path(r'^posts/(?P<year>[0-9]{4})/$', views.posts_por_ano),
]

Templates: Renderizando HTML

Templates separam lógica de apresentação usando a Django Template Language (DTL).

Sintaxe Básica DTL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- templates/blog/lista.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Blog</title>
</head>
<body>
    <h1>Posts do Blog</h1>
    
    {% if posts %}
        <ul>
        {% for post in posts %}
            <li>
                <h2>{{ post.titulo }}</h2>
                <p>{{ post.conteudo|truncatewords:30 }}</p>
                <small>Por {{ post.autor.username }} em {{ post.data_publicacao|date:"d/m/Y" }}</small>
            </li>
        {% endfor %}
        </ul>
    {% else %}
        <p>Nenhum post encontrado.</p>
    {% endif %}
</body>
</html>

Tags DTL Essenciais

  • {% if %} - Condicional
  • {% for %} - Loop
  • {% block %} - Define bloco para herança
  • {% extends %} - Herda de template base
  • {% include %} - Inclui outro template
  • {% url %} - Gera URL reversa
  • {% csrf_token %} - Token CSRF para forms

Filtros Úteis

  • {{ valor|date:"d/m/Y" }} - Formatar data
  • {{ texto|truncatewords:10 }} - Truncar palavras
  • {{ texto|lower }} - Minúsculas
  • {{ texto|upper }} - Maiúsculas
  • {{ lista|length }} - Tamanho da lista
  • {{ valor|default:"N/A" }} - Valor padrão

Template Inheritance (Herança)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Meu Site{% endblock %}</title>
</head>
<body>
    <nav>
        <!-- Navegação -->
    </nav>
    
    <main>
        {% block content %}{% endblock %}
    </main>
    
    <footer>
        <!-- Rodapé -->
    </footer>
</body>
</html>

<!-- templates/blog/lista.html -->
{% extends 'base.html' %}

{% block title %}Posts - {{ block.super }}{% endblock %}

{% block content %}
    <h1>Lista de Posts</h1>
    <!-- Conteúdo específico -->
{% endblock %}

Django 5.1: Suporte completo para Jinja2 como backend de templates alternativo para projetos que precisam de mais flexibilidade.

Artigo relacionado:

Forms: Validação e Processamento

Django Forms facilitam criação e validação de formulários HTML.

Django Forms

1
2
3
4
5
6
7
8
9
10
11
12
13
# forms.py
from django import forms

class ContatoForm(forms.Form):
    nome = forms.CharField(max_length=100)
    email = forms.EmailField()
    mensagem = forms.CharField(widget=forms.Textarea)
    
    def clean_email(self):
        email = self.cleaned_data['email']
        if not email.endswith('@exemplo.com'):
            raise forms.ValidationError('Use e-mail corporativo')
        return email

ModelForms

Cria forms automaticamente baseados em Models:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['titulo', 'conteudo', 'publicado']
        widgets = {
            'conteudo': forms.Textarea(attrs={'rows': 5}),
        }
        labels = {
            'titulo': 'Título do Post',
        }

Processando Forms em Views

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# views.py
from django.shortcuts import render, redirect
from .forms import PostForm

def criar_post(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('lista_posts')
    else:
        form = PostForm()
    
    return render(request, 'blog/criar.html', {'form': form})

Renderizando Forms em Templates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- templates/blog/criar.html -->
<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Salvar</button>
</form>

<!-- Ou com mais controle -->
<form method="post">
    {% csrf_token %}
    {% for field in form %}
        <div class="form-group">
            {{ field.label_tag }}
            {{ field }}
            {% if field.errors %}
                <div class="error">{{ field.errors }}</div>
            {% endif %}
        </div>
    {% endfor %}
    <button type="submit">Salvar</button>
</form>

Artigo relacionado:

Django Admin: Painel Administrativo Poderoso

Uma das features mais impressionantes do Django é o Admin automático.

Configurando o Admin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# admin.py
from django.contrib import admin
from .models import Post

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['titulo', 'autor', 'data_publicacao', 'publicado']
    list_filter = ['publicado', 'data_publicacao']
    search_fields = ['titulo', 'conteudo']
    prepopulated_fields = {'slug': ('titulo',)}
    date_hierarchy = 'data_publicacao'
    ordering = ['-data_publicacao']
    list_editable = ['publicado']
    
    fieldsets = (
        ('Informações Básicas', {
            'fields': ('titulo', 'slug', 'autor')
        }),
        ('Conteúdo', {
            'fields': ('conteudo',)
        }),
        ('Publicação', {
            'fields': ('publicado', 'data_publicacao'),
            'classes': ('collapse',)
        }),
    )

Criando Superusuário

1
python manage.py createsuperuser

Acesse /admin/ e faça login!

Recursos do Admin Django 5.1:

  • Dark Mode automático
  • ✅ Acessibilidade WCAG 2.1 AA
  • ✅ Interface responsiva
  • ✅ Filtros avançados
  • ✅ Busca integrada
  • ✅ Ações em lote customizáveis
  • ✅ Inline editing de relacionamentos

Artigo relacionado:

Middlewares: Processamento de Requisições

Middlewares interceptam requisições/respostas para adicionar funcionalidades globais.

Estrutura de um Middleware

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# middleware.py
class LogMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # Código executado ANTES da view
        print(f'Requisição para: {request.path}')
        
        response = self.get_response(request)
        
        # Código executado DEPOIS da view
        print(f'Status: {response.status_code}')
        
        return response
    
    def process_exception(self, request, exception):
        # Executado quando há exceção
        print(f'Erro: {exception}')
        return None

Async Middlewares (Django 5.1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# middleware.py
class AsyncLogMiddleware:
    async_capable = True
    
    def __init__(self, get_response):
        self.get_response = get_response
    
    async def __call__(self, request):
        # Operações assíncronas
        await self.log_request(request)
        response = await self.get_response(request)
        return response
    
    async def log_request(self, request):
        # Lógica assíncrona
        pass

Registrando Middleware

1
2
3
4
5
6
7
8
9
# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'meu_app.middleware.LogMiddleware',  # Seu middleware
    # ...
]

Artigo relacionado:

Bancos de Dados: PostgreSQL e MySQL

Por padrão, Django usa SQLite para desenvolvimento, mas para produção você deve usar bancos de dados mais robustos como PostgreSQL ou MySQL.

Por que mudar do SQLite?

  • SQLite não suporta múltiplas conexões simultâneas
  • Falta recursos avançados (full-text search, JSON fields complexos)
  • Não é adequado para alta concorrência
  • PostgreSQL/MySQL são otimizados para produção

Configurando PostgreSQL

PostgreSQL é o banco de dados mais recomendado para Django. Ele oferece:

  • Recursos avançados (JSONB, full-text search, arrays)
  • Excelente performance
  • Conformidade com SQL
  • Suporte completo do Django para features específicas

Passo 1: Configure o dicionário DATABASES no seu settings.py:

1
2
3
4
5
6
7
8
9
10
11
# settings.py
DATABASES = {
    'default': {  # Nome da conexão (pode ter múltiplas)
        'ENGINE': 'django.db.backends.postgresql',  # Driver PostgreSQL
        'NAME': 'meu_banco',  # Nome do banco de dados
        'USER': 'usuario',  # Usuário do PostgreSQL
        'PASSWORD': 'senha',  # Senha (use variáveis de ambiente!)
        'HOST': 'localhost',  # Servidor (localhost para dev, IP/domínio para prod)
        'PORT': '5432',  # Porta padrão do PostgreSQL
    }
}

Segurança: Nunca deixe senhas hardcoded! Use variáveis de ambiente: 'PASSWORD': os.environ.get('DB_PASSWORD')

Passo 2: Instale o driver (adaptador) Python para PostgreSQL:

1
2
3
4
5
# psycopg2 (tradicional, estável)
pip install psycopg2-binary

# psycopg3 (Django 5.1+ - recomendado para novos projetos)
pip install psycopg[binary]

Qual driver usar?

  • psycopg2-binary: Versão tradicional, binários pré-compilados. Use em desenvolvimento.
  • psycopg2: Versão para compilar localmente. Use em produção (mais otimizado).
  • psycopg3: Nova geração, suporte async completo, melhor performance. Recomendado para Django 5.1+.

Django 5.1: psycopg3 oferece melhor performance, suporte async nativo e API modernizada. Se está começando um projeto novo, use psycopg3!

Configurando MySQL

MySQL e MariaDB são alternativas populares, especialmente em ambientes de hospedagem compartilhada.

Quando usar MySQL:

  • Já tem expertise com MySQL
  • Infraestrutura existente usa MySQL
  • Hospedagem compartilhada (mais comum que PostgreSQL)
  • Integração com outras ferramentas MySQL

Passo 1: Configure no settings.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # Driver MySQL
        'NAME': 'meu_banco',  # Nome do banco
        'USER': 'usuario',  # Usuário MySQL
        'PASSWORD': 'senha',  # Senha (use variável de ambiente!)
        'HOST': 'localhost',  # Servidor
        'PORT': '3306',  # Porta padrão MySQL
        'OPTIONS': {
            'charset': 'utf8mb4',  # Charset para suportar emojis e caracteres especiais
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",  # Modo estrito
        },
    }
}

Por que utf8mb4? É a versão completa do UTF-8 no MySQL, suportando emojis (🚀) e caracteres de 4 bytes.

Passo 2: Instale o driver MySQL:

1
2
3
4
5
# mysqlclient (recomendado - mais rápido, escrito em C)
pip install mysqlclient

# PyMySQL (alternativa - Python puro, mais fácil de instalar)
pip install pymysql

Qual driver usar?

  • mysqlclient: Mais rápido (código C), mas pode ter problemas de instalação em Windows.
  • PyMySQL: Mais lento, mas 100% Python, instala em qualquer lugar.

Se usar PyMySQL, adicione no __init__.py do projeto:

1
2
import pymysql
pymysql.install_as_MySQLdb()  # Faz PyMySQL se passar por MySQLdb

Django 5.1 requer MySQL 8.0+ ou MariaDB 10.5+. Versões antigas não são mais suportadas!

Artigos relacionados:

Múltiplos Bancos de Dados

Django permite usar múltiplos bancos de dados no mesmo projeto. Isso é útil para:

  • Separar dados de aplicação de analytics
  • Ler de réplicas (read-only databases)
  • Isolar dados de diferentes clientes (multi-tenancy)
  • Migrar gradualmente entre bancos

Exemplo: Aplicação principal em PostgreSQL, analytics em outro banco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# settings.py
DATABASES = {
    'default': {  # Banco principal da aplicação
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'principal',
        'USER': 'app_user',
        'PASSWORD': 'senha',
        'HOST': 'db.exemplo.com',
    },
    'analytics': {  # Banco separado para analytics/relatórios
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'analytics',
        'USER': 'analytics_user',
        'PASSWORD': 'outra_senha',
        'HOST': 'analytics.exemplo.com',
    }
}

Como usar bancos específicos:

1
2
3
4
5
6
7
8
9
10
11
12
# Ler do banco de analytics
posts_analytics = Post.objects.using('analytics').all()

# Salvar no banco de analytics
post = Post(titulo='Teste')
post.save(using='analytics')

# Deletar do banco de analytics
post.delete(using='analytics')

# Query que junta dados de ambos os bancos NÃO é possível!
# Você precisa fazer queries separadas e juntar no Python

Database Routers (Avançado): Para controlar automaticamente qual model vai para qual banco, crie um router:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# database_router.py
class AnalyticsRouter:
    """Direciona models do app 'analytics' para o banco 'analytics'"""
    
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'analytics':
            return 'analytics'
        return None
    
    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'analytics':
            return 'analytics'
        return None

# settings.py
DATABASE_ROUTERS = ['caminho.database_router.AnalyticsRouter']

Django REST Framework: Criando APIs Profissionais

Django REST Framework (DRF) é uma biblioteca poderosa que transforma seu projeto Django em uma API RESTful completa.

O que é uma API REST? REST (Representational State Transfer) é um padrão arquitetural para criar APIs web que usam métodos HTTP (GET, POST, PUT, DELETE) para operar sobre recursos.

Por que usar DRF?

  • Serialização automática de models para JSON
  • Autenticação e permissões prontas
  • Interface web para testar a API (Browsable API)
  • Paginação, filtragem e busca automáticas
  • Documentação automática (com Swagger/OpenAPI)

Instalação

Passo 1: Instale o DRF:

1
pip install djangorestframework

Passo 2: Adicione às apps instaladas:

1
2
3
4
5
6
7
8
9
10
11
# settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    # ... outras apps padrão do Django
    
    'rest_framework',  # Adicione o DRF aqui
    
    # Suas apps
    'blog',
]

Serializers: Convertendo Models em JSON

Serializers são a ponte entre seus Models Django (objetos Python) e JSON (formato usado em APIs).

O que fazem:

  1. Serialização: Model → JSON (para enviar ao cliente)
  2. Desserialização: JSON → Model (ao receber dados do cliente)
  3. Validação: Garantem que dados recebidos estão corretos

Exemplo prático: Vamos criar um serializer para nosso model Post:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# serializers.py
from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    # Campo customizado: pega o username do autor ao invés do ID
    # source='autor.username' = acessa o atributo username do objeto autor
    # read_only=True = não pode ser alterado via API (calculado automaticamente)
    autor_nome = serializers.CharField(
        source='autor.username', 
        read_only=True
    )
    
    # Campo calculado: conta quantos comentários o post tem
    total_comentarios = serializers.SerializerMethodField()
    
    class Meta:
        model = Post  # Model que será serializado
        
        # Campos que serão incluídos no JSON
        fields = [
            'id', 
            'titulo', 
            'conteudo', 
            'autor_nome',  # Campo customizado
            'data_publicacao',
            'total_comentarios'  # Campo calculado
        ]
        
        # Campos que não podem ser alterados via API
        # (são preenchidos automaticamente pelo Django)
        read_only_fields = ['data_publicacao']
    
    def get_total_comentarios(self, obj):
        """Método para o SerializerMethodField"""
        # obj = instância do Post sendo serializada
        return obj.comentarios.count()
    
    def validate_titulo(self, value):
        """
        Validação customizada para o campo titulo.
        Django chama automaticamente validate_<nome_do_campo>().
        """
        if len(value) < 5:
            raise serializers.ValidationError(
                'Título deve ter pelo menos 5 caracteres'
            )
        if 'spam' in value.lower():
            raise serializers.ValidationError(
                'Título contém conteúdo proibido'
            )
        return value
    
    def validate(self, data):
        """
        Validação que envolve múltiplos campos.
        Chamado depois de todas as validações individuais.
        """
        # Exemplo: título e conteúdo não podem ser iguais
        if data.get('titulo') == data.get('conteudo'):
            raise serializers.ValidationError(
                'Título e conteúdo não podem ser idênticos'
            )
        return data

O que acontece quando usamos este serializer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Serialização (Model → JSON)
post = Post.objects.get(id=1)
serializer = PostSerializer(post)
print(serializer.data)
# Output: {
#   'id': 1,
#   'titulo': 'Meu Post',
#   'conteudo': 'Conteúdo...',
#   'autor_nome': 'joao',  # Veio de autor.username
#   'data_publicacao': '2025-07-20T10:00:00Z',
#   'total_comentarios': 5  # Calculado pelo método
# }

# Desserialização (JSON → Model)
data = {
    'titulo': 'Novo Post',
    'conteudo': 'Conteúdo do post...',
}
serializer = PostSerializer(data=data)
if serializer.is_valid():  # Valida os dados
    post = serializer.save(autor=request.user)  # Cria o Post
    print(f'Post criado: {post.id}')
else:
    print(serializer.errors)  # Mostra erros de validação

ViewSets: CRUD Automático

ViewSets são classes que fornecem operações CRUD completas (Create, Read, Update, Delete) com poucas linhas de código.

O que um ViewSet faz automaticamente:

  • list() - GET /api/posts/ (listar todos)
  • create() - POST /api/posts/ (criar novo)
  • retrieve() - GET /api/posts/{id}/ (buscar um)
  • update() - PUT /api/posts/{id}/ (atualizar completo)
  • partial_update() - PATCH /api/posts/{id}/ (atualizar parcial)
  • destroy() - DELETE /api/posts/{id}/ (deletar)

Exemplo prático: ViewSet completo com todas as features:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# views.py
from rest_framework import viewsets, status
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters

from .models import Post
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    """
    ViewSet para gerenciar Posts.
    Fornece endpoints: list, create, retrieve, update, destroy.
    """
    
    # QuerySet base - apenas posts publicados
    queryset = Post.objects.filter(publicado=True).select_related('autor')
    
    # Serializer que será usado para converter dados
    serializer_class = PostSerializer
    
    # Permissões: Leitura pública, escrita apenas autenticado
    permission_classes = [IsAuthenticatedOrReadOnly]
    
    # Habilita filtragem, busca e ordenação
    filter_backends = [
        DjangoFilterBackend,  # Filtragem por campos exatos
        filters.SearchFilter,  # Busca textual
        filters.OrderingFilter  # Ordenação
    ]
    
    # Campos que podem ser filtrados
    # Ex: /api/posts/?autor=5&publicado=true
    filterset_fields = ['autor', 'publicado', 'data_publicacao']
    
    # Campos onde busca textual funciona
    # Ex: /api/posts/?search=django
    search_fields = ['titulo', 'conteudo', 'autor__username']
    
    # Campos que podem ser usados para ordenar
    # Ex: /api/posts/?ordering=-data_publicacao
    ordering_fields = ['data_publicacao', 'titulo']
    ordering = ['-data_publicacao']  # Ordenação padrão
    
    def get_queryset(self):
        """
        Customiza o queryset baseado no usuário.
        Chamado automaticamente pelo DRF.
        """
        queryset = super().get_queryset()
        
        # Se usuário autenticado, mostra seus posts não publicados também
        if self.request.user.is_authenticated:
            queryset = Post.objects.filter(
                publicado=True
            ) | Post.objects.filter(
                autor=self.request.user
            )
        
        return queryset
    
    def perform_create(self, serializer):
        """
        Customiza a criação de um post.
        Define o autor automaticamente como o usuário logado.
        """
        serializer.save(autor=self.request.user)
    
    @action(detail=True, methods=['post'])
    def publicar(self, request, pk=None):
        """
        Ação customizada: publicar um post.
        Acessível em: POST /api/posts/{id}/publicar/
        """
        post = self.get_object()
        
        # Verifica se usuário é o autor
        if post.autor != request.user:
            return Response(
                {'erro': 'Apenas o autor pode publicar'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        post.publicado = True
        post.save()
        
        serializer = self.get_serializer(post)
        return Response(serializer.data)
    
    @action(detail=False, methods=['get'])
    def meus_posts(self, request):
        """
        Ação customizada: listar posts do usuário logado.
        Acessível em: GET /api/posts/meus_posts/
        """
        posts = Post.objects.filter(autor=request.user)
        
        # Pagina os resultados
        page = self.paginate_queryset(posts)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)
        
        serializer = self.get_serializer(posts, many=True)
        return Response(serializer.data)

O que cada parte faz:

  • get_queryset(): Customiza quais objetos são retornados
  • perform_create(): Adiciona lógica extra ao criar
  • @action(): Cria endpoints customizados além do CRUD padrão
  • detail=True: Ação funciona em um objeto específico (/posts/{id}/publicar/)
  • detail=False: Ação funciona na coleção (/posts/meus_posts/)

Routers: URLs Automáticas

Routers do DRF criam automaticamente todas as URLs necessárias para seus ViewSets.

Sem Router (modo manual - trabalhoso):

1
2
3
4
5
6
7
8
9
10
# urls.py - EVITE FAZER ASSIM!
urlpatterns = [
    path('posts/', PostViewSet.as_view({'get': 'list', 'post': 'create'})),
    path('posts/<int:pk>/', PostViewSet.as_view({
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'destroy'
    })),
]

Com Router (recomendado - automático):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet

# Cria o router
router = DefaultRouter()

# Registra o ViewSet
# 'posts' = prefixo da URL
# PostViewSet = a classe ViewSet
router.register(r'posts', PostViewSet, basename='post')

# Inclui as URLs geradas pelo router
urlpatterns = [
    path('api/', include(router.urls)),
]

URLs geradas automaticamente pelo Router:

Método HTTP URL Ação Descrição
GET /api/posts/ list Lista todos os posts
POST /api/posts/ create Cria novo post
GET /api/posts/{id}/ retrieve Busca post específico
PUT /api/posts/{id}/ update Atualiza post completo
PATCH /api/posts/{id}/ partial_update Atualiza parcialmente
DELETE /api/posts/{id}/ destroy Deleta post
POST /api/posts/{id}/publicar/ publicar Ação custom
GET /api/posts/meus_posts/ meus_posts Ação custom

Exemplo de uso da API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Listar todos os posts
curl http://localhost:8000/api/posts/

# Buscar um post específico
curl http://localhost:8000/api/posts/5/

# Criar novo post (precisa autenticação)
curl -X POST http://localhost:8000/api/posts/ \
  -H "Authorization: Token seu-token-aqui" \
  -H "Content-Type: application/json" \
  -d '{"titulo": "Novo Post", "conteudo": "Conteúdo..."}'

# Filtrar posts por autor
curl http://localhost:8000/api/posts/?autor=5

# Buscar por texto
curl http://localhost:8000/api/posts/?search=django

# Ordenar por data
curl http://localhost:8000/api/posts/?ordering=-data_publicacao

Autenticação e Permissões

DRF oferece autenticação (quem é o usuário?) e permissões (o que ele pode fazer?).

Tipos de Autenticação:

  • SessionAuthentication: Usa sessões Django (cookies)
  • TokenAuthentication: Token fixo por usuário
  • JWTAuthentication: JSON Web Tokens (mais seguro)
  • BasicAuthentication: HTTP Basic (username:password em base64)

Tipos de Permissões:

  • AllowAny: Acesso público total
  • IsAuthenticated: Apenas usuários logados
  • IsAdminUser: Apenas administradores
  • IsAuthenticatedOrReadOnly: Leitura pública, escrita autenticada

Configuração global:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# settings.py
REST_FRAMEWORK = {
    # Métodos de autenticação aceitos
    'DEFAULT_AUTHENTICATION_CLASSES': [
        # Token: usuário envia "Authorization: Token abc123"
        'rest_framework.authentication.TokenAuthentication',
        # Session: usa cookies do Django (para Browsable API)
        'rest_framework.authentication.SessionAuthentication',
    ],
    
    # Permissão padrão: apenas usuários autenticados
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    
    # Paginação automática
    'DEFAULT_PAGINATION_CLASS': (
        'rest_framework.pagination.PageNumberPagination'
    ),
    'PAGE_SIZE': 20,  # 20 itens por página
    
    # Formatos de resposta aceitos
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',  # JSON
        'rest_framework.renderers.BrowsableAPIRenderer',  # HTML
    ],
    
    # Throttling (limite de requisições)
    'DEFAULT_THROTTLE_CLASSES': [
        'rest_framework.throttling.AnonRateThrottle',  # Anônimos
        'rest_framework.throttling.UserRateThrottle',  # Autenticados
    ],
    'DEFAULT_THROTTLE_RATES': {
        'anon': '100/day',  # 100 requisições por dia
        'user': '1000/day',  # 1000 requisições por dia
    },
}

Usando Token Authentication:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Adicione 'rest_framework.authtoken' ao INSTALLED_APPS
INSTALLED_APPS = [
    # ...
    'rest_framework',
    'rest_framework.authtoken',  # Para usar tokens
]

# Rode as migrations
# python manage.py migrate

# Crie tokens para usuários
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User

user = User.objects.get(username='joao')
token = Token.objects.create(user=user)
print(token.key)  # "9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b"

# Cliente usa o token:
# Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

Permissões customizadas:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# permissions.py
from rest_framework import permissions

class IsAuthorOrReadOnly(permissions.BasePermission):
    """
    Permissão customizada: apenas o autor pode editar.
    """
    
    def has_object_permission(self, request, view, obj):
        # Leitura permitida para todos
        if request.method in permissions.SAFE_METHODS:  # GET, HEAD, OPTIONS
            return True
        
        # Escrita apenas para o autor
        return obj.autor == request.user

# views.py
from .permissions import IsAuthorOrReadOnly

class PostViewSet(viewsets.ModelViewSet):
    # ...
    permission_classes = [IsAuthenticated, IsAuthorOrReadOnly]

Browsable API

DRF fornece interface web interativa automaticamente:

1
2
# Acesse no navegador
http://localhost:8000/api/posts/

Features da Browsable API:

  • ✅ Interface amigável para testar endpoints
  • ✅ Autenticação integrada
  • ✅ Formulários para POST/PUT
  • ✅ Dark mode (DRF 3.15+)

Artigo relacionado:

Comandos manage.py Essenciais

O manage.py é sua interface de linha de comando para tudo no Django.

Comandos de Desenvolvimento

1
2
3
4
5
6
7
8
9
10
11
12
# Iniciar servidor de desenvolvimento
python manage.py runserver
python manage.py runserver 0.0.0.0:8080  # Porta customizada

# Criar nova app
python manage.py startapp nome_da_app

# Shell Python com Django carregado
python manage.py shell

# Shell do banco de dados
python manage.py dbshell

Comandos de Banco de Dados

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Criar migrations
python manage.py makemigrations
python manage.py makemigrations nome_da_app  # App específica

# Aplicar migrations
python manage.py migrate
python manage.py migrate nome_da_app  # App específica

# Reverter migration
python manage.py migrate nome_da_app 0003  # Volta para migration 0003

# Ver migrations
python manage.py showmigrations

# SQL da migration (sem executar)
python manage.py sqlmigrate nome_da_app 0001

Comandos de Admin

1
2
3
4
5
# Criar superusuário
python manage.py createsuperuser

# Alterar senha de usuário
python manage.py changepassword username

Comandos de Arquivos Estáticos

1
2
3
4
5
# Coletar arquivos estáticos para produção
python manage.py collectstatic

# Coletar sem confirmação
python manage.py collectstatic --noinput

Comandos de Testes

1
2
3
4
5
6
7
8
# Executar todos os testes
python manage.py test

# Testar app específica
python manage.py test nome_da_app

# Teste com cobertura
python manage.py test --with-coverage

Comandos de Verificação

1
2
3
4
5
# Verificar projeto inteiro
python manage.py check

# Verificar se há migrations pendentes (CI/CD)
python manage.py makemigrations --check --dry-run

Artigo relacionado:

Boas Práticas Django 2025

Estrutura de Projeto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
projeto/
├── apps/
│   ├── blog/
│   │   ├── migrations/
│   │   ├── templates/blog/
│   │   ├── static/blog/
│   │   ├── models.py
│   │   ├── views.py
│   │   ├── urls.py
│   │   ├── admin.py
│   │   ├── forms.py
│   │   └── tests.py
│   └── usuarios/
├── config/
│   ├── settings/
│   │   ├── base.py
│   │   ├── development.py
│   │   └── production.py
│   ├── urls.py
│   └── wsgi.py
├── static/
├── media/
├── templates/
├── requirements/
│   ├── base.txt
│   ├── development.txt
│   └── production.txt
└── manage.py

Settings Separados por Ambiente

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# config/settings/base.py
# Configurações comuns

# config/settings/development.py
from .base import *

DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1']

# config/settings/production.py
from .base import *

DEBUG = False
ALLOWED_HOSTS = ['meusite.com']
SECURE_SSL_REDIRECT = True
1
2
# Usar settings específico
python manage.py runserver --settings=config.settings.development

Variáveis de Ambiente

Use python-decouple ou django-environ:

1
2
3
4
5
6
# settings.py
from decouple import config

SECRET_KEY = config('SECRET_KEY')
DEBUG = config('DEBUG', default=False, cast=bool)
DATABASE_URL = config('DATABASE_URL')
1
2
3
4
# .env
SECRET_KEY=sua-chave-secreta-aqui
DEBUG=True
DATABASE_URL=postgresql://user:pass@localhost/db

Segurança

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# settings.py (Produção)

# HTTPS
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True

# HSTS
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

# Outras
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_BROWSER_XSS_FILTER = True
X_FRAME_OPTIONS = 'DENY'

Performance

1. Use select_related e prefetch_related

1
2
3
4
5
6
7
8
9
# ❌ Ruim - N+1 queries
posts = Post.objects.all()
for post in posts:
    print(post.autor.username)  # Query a cada iteração

# ✅ Bom - 1 query com JOIN
posts = Post.objects.select_related('autor').all()
for post in posts:
    print(post.autor.username)  # Sem queries extras

2. Use Cache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# settings.py
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
    }
}

# views.py
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)  # Cache por 15 minutos
def lista_posts(request):
    posts = Post.objects.all()
    return render(request, 'lista.html', {'posts': posts})

3. Use Database Indexing

1
2
3
4
5
6
7
8
class Post(models.Model):
    titulo = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(unique=True, db_index=True)
    
    class Meta:
        indexes = [
            models.Index(fields=['publicado', '-data_publicacao']),
        ]

4. Use Django Debug Toolbar (Dev)

1
pip install django-debug-toolbar
1
2
3
4
5
6
7
8
9
10
11
12
# settings.py (development)
INSTALLED_APPS = [
    # ...
    'debug_toolbar',
]

MIDDLEWARE = [
    'debug_toolbar.middleware.DebugToolbarMiddleware',
    # ...
]

INTERNAL_IPS = ['127.0.0.1']

Testes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# tests.py
from django.test import TestCase
from .models import Post

class PostModelTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        Post.objects.create(titulo='Teste', conteudo='Conteúdo teste')
    
    def test_titulo_max_length(self):
        post = Post.objects.get(id=1)
        max_length = post._meta.get_field('titulo').max_length
        self.assertEqual(max_length, 200)
    
    def test_str_representation(self):
        post = Post.objects.get(id=1)
        self.assertEqual(str(post), 'Teste')

class PostViewTest(TestCase):
    def test_lista_posts_status_code(self):
        response = self.client.get('/posts/')
        self.assertEqual(response.status_code, 200)
    
    def test_lista_posts_template(self):
        response = self.client.get('/posts/')
        self.assertTemplateUsed(response, 'blog/lista.html')

Async Views (Django 5.1)

Django 5.1 trouxe suporte completo para programação assíncrona. Views assíncronas são úteis quando:

  • Você precisa fazer múltiplas requisições externas (APIs, microsserviços)
  • Operações de I/O não-bloqueantes (ler arquivos, acessar banco de dados)
  • WebSockets e conexões de longa duração
  • Alta concorrência com poucos recursos

Diferença sync vs async:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# ❌ SYNC (bloqueante) - 1 requisição por vez
import requests

def buscar_dados(request):
    # Bloqueia por 2 segundos esperando resposta
    r1 = requests.get('https://api1.com/dados')  # 2s
    r2 = requests.get('https://api2.com/dados')  # 2s
    r3 = requests.get('https://api3.com/dados')  # 2s
    # Total: 6 segundos (uma após a outra)
    return JsonResponse({'dados': [r1.json(), r2.json(), r3.json()]})

# ✅ ASYNC (não-bloqueante) - múltiplas requisições simultaneamente
import httpx
import asyncio

async def buscar_dados_async(request):
    async with httpx.AsyncClient() as client:
        # Executa as 3 requisições AO MESMO TEMPO
        tasks = [
            client.get('https://api1.com/dados'),
            client.get('https://api2.com/dados'),
            client.get('https://api3.com/dados'),
        ]
        responses = await asyncio.gather(*tasks)
        # Total: ~2 segundos (todas em paralelo)
        dados = [r.json() for r in responses]
        return JsonResponse({'dados': dados})

Function-Based Async View:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# views.py
from django.http import JsonResponse
import httpx
import asyncio

async def buscar_dados_externos(request):
    """
    View assíncrona que consulta múltiplas APIs externas.
    Django 5.1 detecta automaticamente que é async (async def).
    """
    
    async with httpx.AsyncClient() as client:
        # Consulta várias APIs em paralelo
        clima_task = client.get('https://api.clima.com/cidade/SP')
        noticias_task = client.get('https://api.noticias.com/ultimas')
        cotacao_task = client.get('https://api.moedas.com/USD')
        
        # Aguarda todas terminarem (em paralelo)
        clima, noticias, cotacao = await asyncio.gather(
            clima_task,
            noticias_task,
            cotacao_task
        )
    
    return JsonResponse({
        'clima': clima.json(),
        'noticias': noticias.json(),
        'cotacao': cotacao.json(),
    })

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    # Django roda automaticamente em modo async
    path('dados/', views.buscar_dados_externos),
]

Class-Based Async View:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# views.py
from django.views import View
from django.http import JsonResponse
import httpx

class AsyncDashboardView(View):
    """
    CBV assíncrona: todos os métodos HTTP podem ser async.
    """
    
    async def get(self, request):
        """
        Método GET assíncrono.
        """
        # Busca dados do banco de forma assíncrona
        from .models import Post
        from asgiref.sync import sync_to_async
        
        # Converte operações sync do ORM para async
        posts = await sync_to_async(list)(
            Post.objects.filter(publicado=True)[:10]
        )
        
        # Busca dados externos
        async with httpx.AsyncClient() as client:
            stats = await client.get('https://api.analytics.com/stats')
        
        return JsonResponse({
            'posts_count': len(posts),
            'stats': stats.json(),
        })
    
    async def post(self, request):
        """
        Método POST assíncrono.
        """
        import json
        data = json.loads(request.body)
        
        # Processa dados de forma assíncrona
        result = await self.processar_async(data)
        
        return JsonResponse({'resultado': result})
    
    async def processar_async(self, data):
        """Método auxiliar assíncrono"""
        # Simula processamento pesado
        await asyncio.sleep(0.1)
        return f"Processado: {data}"

Async com Django ORM:

O ORM do Django é síncrono por padrão. Use sync_to_async para integrar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# views.py
from django.http import JsonResponse
from asgiref.sync import sync_to_async
from .models import Post

async def lista_posts_async(request):
    """
    View assíncrona que consulta o banco de dados.
    """
    
    # Converte query síncrona para assíncrona
    @sync_to_async
    def get_posts():
        return list(Post.objects.filter(publicado=True).values(
            'id', 'titulo', 'data_publicacao'
        ))
    
    # Executa de forma assíncrona
    posts = await get_posts()
    
    return JsonResponse({'posts': posts}, safe=False)

# Ou use diretamente:
async def criar_post_async(request):
    # Converte operação de criação para async
    post = await sync_to_async(Post.objects.create)(
        titulo='Post Async',
        conteudo='Criado de forma assíncrona',
        autor=request.user
    )
    
    return JsonResponse({'id': post.id, 'titulo': post.titulo})

Quando usar Async:

Use async quando:

  • Fazer múltiplas requisições HTTP externas
  • WebSockets e Server-Sent Events
  • Streaming de dados
  • I/O pesado (upload/download de arquivos)
  • Integração com sistemas externos

NÃO use async quando:

  • Operações CPU-bound (cálculos pesados)
  • CRUD simples do Django
  • Queries normais do ORM
  • A view é rápida e simples

Configuração ASGI para Async:

1
2
3
4
5
6
7
8
9
# asgi.py (já vem configurado no Django 5.1)
import os
from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'projeto.settings')
application = get_asgi_application()

# Rodar com servidor ASGI (uvicorn, daphne, hypercorn)
# uvicorn projeto.asgi:application --reload

Instale servidor ASGI:

1
2
3
4
5
6
7
# Uvicorn (recomendado)
pip install uvicorn
python -m uvicorn projeto.asgi:application --reload

# Ou Daphne (do Django Channels)
pip install daphne
daphne projeto.asgi:application

Deploy e Produção

Checklist de Produção

1
2
# Verificar segurança
python manage.py check --deploy

Checklist:

  • DEBUG = False
  • SECRET_KEY em variável de ambiente
  • ALLOWED_HOSTS configurado
  • ✅ HTTPS habilitado
  • ✅ Banco de dados de produção (PostgreSQL/MySQL)
  • ✅ Arquivos estáticos servidos por CDN/Nginx
  • ✅ Media files em storage externo (S3)
  • ✅ Logging configurado
  • ✅ Backups automatizados
  • ✅ Monitoring (Sentry, New Relic)

Servindo com Gunicorn

1
2
3
4
5
6
7
8
pip install gunicorn

# WSGI (tradicional)
gunicorn config.wsgi:application --bind 0.0.0.0:8000

# ASGI (async - Django 5.1)
pip install gunicorn[gevent]
gunicorn config.asgi:application -k uvicorn.workers.UvicornWorker

Arquivos Estáticos

1
2
3
4
5
6
# settings.py
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'mediafiles'
1
python manage.py collectstatic

Nginx Config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
    listen 80;
    server_name meusite.com;
    
    location /static/ {
        alias /var/www/projeto/staticfiles/;
    }
    
    location /media/ {
        alias /var/www/projeto/mediafiles/;
    }
    
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Recursos Adicionais

Documentação Oficial

Ferramentas Recomendadas

  • django-extensions - Comandos úteis extras
  • django-debug-toolbar - Debug em desenvolvimento
  • celery - Tarefas assíncronas
  • django-cors-headers - CORS para APIs
  • django-filter - Filtros avançados
  • drf-spectacular - OpenAPI/Swagger para DRF

Comunidade

Conclusão

Django em 2025 é mais poderoso do que nunca.

Com Django 5.1 LTS, você tem:

  • ✅ Suporte async completo (views, middlewares, ORM)
  • ✅ Segurança de nível enterprise
  • ✅ ORM extremamente eficiente
  • ✅ Admin moderno (dark mode, acessível)
  • ✅ Ecossistema rico (DRF, Celery, Channels)
  • ✅ Comunidade ativa e documentação excepcional

Este guia cobriu desde a instalação até deploy em produção, passando por:

  • Models e ORM
  • Views (FBV e CBV)
  • Templates (DTL)
  • Forms e validação
  • Admin customizado
  • Middlewares
  • Bancos de dados (PostgreSQL/MySQL)
  • Django REST Framework
  • Comandos manage.py
  • Boas práticas
  • Performance e segurança

Próximos passos:

  1. Pratique criando um projeto completo
  2. Estude Django REST Framework em profundidade
  3. Aprenda deploy com Docker
  4. Explore Django Channels (WebSockets)
  5. Contribua para projetos open source Django

Django é uma escolha sólida para qualquer tipo de aplicação web em 2025: desde blogs simples até plataformas complexas com milhões de usuários.

Agora é com você! :rocket:

Quer aprofundar ainda mais? Baixe nosso ebook GRÁTIS de Desenvolvimento Web com Python e Django:

Ebook GRÁTIS

DESENVOLVIMENTO WEB COM PYTHON E DJANGO

Capa Ebook Desenvolvimento Web com Python e Django

Conteúdo:

  • :point_right: Veja como modelar sua aplicação
  • :point_right: Utilize a robusta API de Acesso a Dados do Django
  • :point_right: Aprenda sobre Class Based Views
  • :point_right: Construa Middlewares próprios
  • :point_right: Desenvolva filtros e tags customizados para criar lindos templates
Baixe já!

Nos vemos no código! :wave:

Começe agora sua Jornada na Programação!

Não deixe para amanhã o sucesso que você pode começar a construir hoje!

#newsletter Olá :wave: Curtiu o artigo? Então faça parte da nossa Newsletter! Privacidade Não se preocupe, respeitamos sua privacidade. Você pode se descadastrar a qualquer momento.