O controle condicional de visualizações é uma técnica essencial para otimizar o desempenho de aplicações web, reduzindo a carga no servidor e economizando largura de banda. Em vez de reenviar recursos inteiros em cada requisição, o Django permite que o cliente valide se sua cópia em cache ainda é válida — usando mecanismos definidos pela especificação HTTP: Last-Modified e ETag. Além disso, para garantir integridade e autenticidade de dados transitando por canais inseguros, o framework oferece um sistema robusto de assinatura criptográfica.
Last-Modified: Validação Baseada em Timestamp
Quando um cliente acessa um recurso pela primeira vez, o servidor pode incluir o cabeçalho Last-Modified na resposta, indicando a data e hora da última alteração do recurso (no formato RFC 1123):
Last-Modified: Wed, 04 Oct 2023 15:22:17 GMT
Nas requisições subsequentes, o navegador envia o cabeçalho If-Modified-Since com esse valor. Se o recurso não foi modificado desde então, o servidor responde com status 304 Not Modified e corpo vazio — evitando transmissão redundante.
ETag: Identificação Única por Conteúdo
O ETag é um identificador opcioanl, gerado pelo servidor, que representa uma versão específica do recurso — geralmente calculado a partir do conteúdo (ex: hash SHA-256) ou de metadados relevantes. Ele resolve limitações do Last-Modified, como:
- Arquivos cuja data de modificação muda sem alteração real no conteúdo;
- Atualizações mais frequentes que um segundo (MTIME não distingue subsegundos);
- Sistemas onde timestamps não são confiáveis ou indisponíveis.
O fluxo típico com If-None-Match:
- Requisição inicial → servidor retorna
ETag: "abc123"e status200; - Requisição subsequente → cliente envia
If-None-Match: "abc123"; - O servidor compara e, se coincidir, responde com
304.
Observação: Last-Modified e ETag podem coexistir — ambos são verificados independentemente, e a resposta 304 só ocorre se todas as condições condicionais forem satisfeitas.
Implementação no Django
O Django fornece o decorador @condition, que aceita funções para gerar ETag e/ou Last-Modified. Exemplo prático:
from django.views.decorators.http import condition
from django.http import HttpResponse
from myapp.models import Article
def get_article_last_modified(request, article_id):
try:
return Article.objects.get(id=article_id).updated_at
except Article.DoesNotExist:
return None
def compute_article_etag(request, article_id):
try:
article = Article.objects.get(id=article_id)
return f"{article.id}-{article.updated_at.timestamp()}"
except Article.DoesNotExist:
return None
@condition(
last_modified_func=get_article_last_modified,
etag_func=compute_article_etag
)
def article_detail(request, article_id):
article = Article.objects.get(id=article_id)
return HttpResponse(article.content)
Para casos mais simples, use os decoradores especializados:
from django.views.decorators.http import last_modified, etag
@last_modified(get_article_last_modified)
def article_list(request):
# ...
@etag(compute_article_etag)
def article_preview(request, slug):
# ...
Assinaturas Criptográficas no Django
Quando dados sensíveis precisam ser transmitidos por canais não confiáveis (ex: URLs de redefinição de senha, campos ocultos em formulários), o Django oferece APIs de assinatura que garantem integridade e, opcionalmente, temopralidade.
Assinatura Básica
O módulo django.core.signing fornece a classe Signer, que usa a chave secreta do projeto (SECRET_KEY) para gerar e verificar assinaturas:
from django.core.signing import Signer
signer = Signer()
signed_value = signer.sign("user_42")
# Resulta em: "user_42:ZQVd9XJqGkRwYtLcKbHvNpMxY"
original = signer.unsign(signed_value) # Retorna "user_42"
Uso de Salt (Sal)
O parâmetro salt adiciona variação ao processo de assinatura, impedindo que entradas idênticas gerem assinaturas idênticas — útil para evitar ataques de análise estatística:
signer_v1 = Signer(salt="auth_token")
signer_v2 = Signer(salt="password_reset")
print(signer_v1.sign("test")) # Diferente de signer_v2.sign("test")
Assinaturas com Timestamp
A classe TimestampSigner incorpora um carimbo de tempo cifrado. Isso permite impor validade temporal:
from django.core.signing import TimestampSigner
ts_signer = TimestampSigner()
token = ts_signer.sign("session:789")
# Verifica se o token tem até 300 segundos de idade
try:
payload = ts_signer.unsign(token, max_age=300)
except SignatureExpired:
# Tratar expiração
pass
Serialização Estruturada
Para estruturas complexas (dicionários, listas), use dumps() e loads(), que combinam serialização JSON com assinatura:
from django.core.signing import dumps, loads
data = {"user_id": 101, "role": "admin", "issued_at": 1717023456}
secure_payload = dumps(data)
# Mais tarde...
restored = loads(secure_payload) # Retorna o dicionário original
Essas funções também suportam compressão automática para payloads grandes e personalização de salt e key.