Conceito Fundamental

O que é o Django?

Conheça o framework web Python que alimenta Instagram, Pinterest e milhares de aplicações no mundo todo.

Definição simples

Django é um framework web de alto nível escrito em Python que incentiva o desenvolvimento rápido e design limpo e pragmático. Ele foi criado para que desenvolvedores possam construir aplicações web complexas sem reinventar a roda.

O slogan oficial é: "The web framework for perfectionists with deadlines" — o framework web para perfeccionistas com prazo. Isso resume bem a filosofia: Django é opinativo (tem um jeito certo de fazer as coisas) mas extremamente produtivo. Você passa o tempo construindo features, não configurando infraestrutura básica.

Django foi criado em 2003 por Adrian Holovaty e Simon Willison para o jornal Lawrence Journal-World, e é mantido pela Django Software Foundation. Está na versão 5.x e tem compatibilidade garantida de longo prazo nas versões LTS.

Por que usar Django?

  • Batteries included: traz ORM, autenticação, admin, sistema de migrations, cache, formulários e muito mais nativamente
  • Segurança: protege contra CSRF, XSS, SQL Injection e clickjacking por padrão
  • Escalável: usado pelo Instagram com bilhões de usuários
  • Comunidade enorme: milhares de pacotes (PyPI) e anos de documentação de alta qualidade
  • ORM poderoso: trabalhe com o banco de dados usando Python puro, sem escrever SQL na maioria dos casos
  • Admin automático: painel de gerenciamento gerado automaticamente a partir dos seus models
  • Documentação excelente: a documentação oficial do Django é referência na indústria

O padrão MTV

Django usa o padrão MTV (Model–Template–View), similar ao MVC tradicional:

  • Model: define a estrutura dos dados e se comunica com o banco de dados via ORM
  • Template: é o HTML que o usuário vê (em APIs REST, é substituído por JSON)
  • View: contém a lógica de negócio, processa requisições HTTP e retorna respostas

O fluxo de uma requisição Django funciona assim: o usuário acessa uma URL → o Django consulta ourls.py para encontrar a view responsável → a view processa a lógica (consulta models, valida dados) → retorna uma resposta (HTML via template ou JSON via DRF).

Estrutura de um projeto Django

meu_projeto/
├── manage.py              # CLI do Django (runserver, migrate, etc.)
├── requirements.txt       # dependências Python
├── .env                   # variáveis de ambiente (nunca comite!)
├── meu_projeto/           # pacote de configurações
│   ├── __init__.py
│   ├── settings.py        # todas as configurações
│   ├── urls.py            # roteamento principal
│   ├── wsgi.py            # servidor WSGI (produção)
│   └── asgi.py            # servidor ASGI (Channels/async)
└── blog/                  # um app Django
    ├── __init__.py
    ├── admin.py           # configuração do admin
    ├── apps.py            # configuração do app
    ├── models.py          # seus models (banco de dados)
    ├── views.py           # lógica das páginas
    ├── urls.py            # rotas do app
    ├── serializers.py     # para DRF (APIs)
    ├── tests.py           # testes
    └── migrations/        # migrations geradas pelo Django
        └── 0001_initial.py

Cada app Django é um módulo independente que encapsula uma funcionalidade. Um projeto pode ter múltiplos apps: users, blog, payments, etc. A boa prática é manter cada app focado em uma responsabilidade.

Exemplo de Model

Criar uma tabela no banco de dados é simplesmente criar uma classe Python que herda de models.Model. O Django cuida de toda a lógica SQL por baixo:

from django.db import models
from django.contrib.auth import get_user_model
from django.utils.text import slugify

User = get_user_model()

class Categoria(models.Model):
    nome = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(unique=True, blank=True)

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.nome)
        super().save(*args, **kwargs)

    def __str__(self):
        return self.nome

class Post(models.Model):
    titulo = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    conteudo = models.TextField()
    resumo = models.CharField(max_length=300, blank=True)
    autor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    categoria = models.ForeignKey(Categoria, on_delete=models.SET_NULL, null=True, blank=True)
    publicado_em = models.DateTimeField(auto_now_add=True)
    atualizado_em = models.DateTimeField(auto_now=True)
    publicado = models.BooleanField(default=False)
    visualizacoes = models.PositiveIntegerField(default=0)

    class Meta:
        ordering = ['-publicado_em']
        verbose_name = 'Post'
        verbose_name_plural = 'Posts'

    def __str__(self):
        return self.titulo

    @property
    def tempo_leitura(self):
        palavras = len(self.conteudo.split())
        return max(1, round(palavras / 200))  # 200 palavras/minuto

Views e URLs

As views no Django processam requisições e retornam respostas. Existem duas formas principais:function-based views (FBV) e class-based views (CBV).

# views.py — function-based view (simples e direta)
from django.shortcuts import render, get_object_or_404
from .models import Post

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

def detalhe_post(request, slug):
    post = get_object_or_404(Post, slug=slug, publicado=True)
    post.visualizacoes += 1
    post.save(update_fields=['visualizacoes'])
    return render(request, 'blog/detalhe.html', {'post': post})


# views.py — class-based view (mais recursos, menos código para CRUD)
from django.views.generic import ListView, DetailView
from django.contrib.auth.mixins import LoginRequiredMixin

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).select_related('autor')
# urls.py do app blog
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.PostListView.as_view(), name='lista'),
    path('<slug:slug>/', views.detalhe_post, name='detalhe'),
]

# urls.py principal (meu_projeto/urls.py)
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls', namespace='blog')),
    path('api/', include('blog.api_urls')),  # para DRF
]

ORM do Django — Consultando o banco

O ORM do Django permite consultar o banco de dados com Python puro. Aqui estão as operações mais comuns:

# Buscar todos os posts publicados
posts = Post.objects.filter(publicado=True)

# Buscar um único post (levanta Post.DoesNotExist se não achar)
post = Post.objects.get(slug='meu-post')

# Buscar ou 404 (atalho para views)
post = get_object_or_404(Post, slug='meu-post', publicado=True)

# Filtros avançados
posts_recentes = Post.objects.filter(
    publicado=True,
    publicado_em__gte=timezone.now() - timedelta(days=30)
).order_by('-publicado_em')

# Select relacionados (evita N+1 queries)
posts = Post.objects.select_related('autor', 'categoria').prefetch_related('tags')

# Agregar dados
from django.db.models import Count, Avg
stats = Post.objects.aggregate(
    total=Count('id'),
    media_visualizacoes=Avg('visualizacoes')
)

# Anotar queryset
posts = Post.objects.annotate(
    num_comentarios=Count('comentarios')
).order_by('-num_comentarios')

# Criar
post = Post.objects.create(
    titulo='Novo Post',
    slug='novo-post',
    conteudo='Conteúdo...',
    autor=request.user,
    publicado=True,
)

# Atualizar em massa
Post.objects.filter(autor=user).update(publicado=False)

# Deletar
Post.objects.filter(publicado=False, publicado_em__lt=cutoff).delete()

Django Admin

Um dos recursos mais amados do Django é o Admin automático. Com poucas linhas, você tem um painel completo de gerenciamento de dados:

from django.contrib import admin
from .models import Post, Categoria

@admin.register(Categoria)
class CategoriaAdmin(admin.ModelAdmin):
    list_display = ['nome', 'slug']
    prepopulated_fields = {'slug': ('nome',)}
    search_fields = ['nome']

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['titulo', 'autor', 'categoria', 'publicado', 'publicado_em', 'visualizacoes']
    list_filter = ['publicado', 'categoria', 'publicado_em']
    search_fields = ['titulo', 'conteudo', 'autor__username']
    prepopulated_fields = {'slug': ('titulo',)}
    date_hierarchy = 'publicado_em'
    ordering = ['-publicado_em']
    raw_id_fields = ['autor']

    # Ações personalizadas
    actions = ['publicar_posts', 'despublicar_posts']

    @admin.action(description='Publicar posts selecionados')
    def publicar_posts(self, request, queryset):
        queryset.update(publicado=True)

    @admin.action(description='Despublicar posts selecionados')
    def despublicar_posts(self, request, queryset):
        queryset.update(publicado=False)

Migrations — versionando o banco de dados

O sistema de migrations do Django rastreia todas as mudanças nos seus models e as aplica ao banco de dados de forma controlada. É como um sistema de controle de versão para o schema do banco.

# Após modificar um model, gere a migration:
python manage.py makemigrations

# Veja o SQL que será executado (sem aplicar):
python manage.py sqlmigrate blog 0001

# Aplique as migrations ao banco:
python manage.py migrate

# Ver status das migrations:
python manage.py showmigrations

# Reverter uma migration (cuidado!):
python manage.py migrate blog 0001  # volta para a migration 0001

As migrations são arquivos Python que ficam no diretório migrations/ de cada app. Sempre comite as migrations junto com as mudanças nos models — elas fazem parte do código.

Como criar um projeto Django do zero

  1. Crie um ambiente virtual: python -m venv venv && source venv/bin/activate
  2. Instale as dependências: pip install django djangorestframework django-allauth dj-rest-auth
  3. Crie o projeto: django-admin startproject meu_projeto .
  4. Crie um app: python manage.py startapp blog
  5. Configure o settings.py (banco, apps instalados, etc.)
  6. Rode as migrations iniciais: python manage.py migrate
  7. Crie um superusuário: python manage.py createsuperuser
  8. Rode o servidor: python manage.py runserver
# settings.py — configurações essenciais para produção
import environ
env = environ.Env()
environ.Env.read_env()

SECRET_KEY = env('SECRET_KEY')
DEBUG = env.bool('DEBUG', default=False)
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')

DATABASES = {
    'default': env.db('DATABASE_URL')  # ex: postgresql://user:pass@host/db
}

# Arquivos estáticos
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

# Internacionalização
LANGUAGE_CODE = 'pt-br'
TIME_ZONE = 'America/Sao_Paulo'
USE_I18N = True
USE_TZ = True

Signals — reagindo a eventos do Django

Signals permitem que partes desacopladas da aplicação sejam notificadas quando certas ações ocorrem, como salvar ou deletar um objeto:

from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from .models import Post, Perfil

User = get_user_model()

# Criar perfil automaticamente quando um usuário é criado
@receiver(post_save, sender=User)
def criar_perfil(sender, instance, created, **kwargs):
    if created:
        Perfil.objects.create(usuario=instance)

# Invalidar cache quando um post é salvo
@receiver(post_save, sender=Post)
def invalidar_cache_post(sender, instance, **kwargs):
    from django.core.cache import cache
    cache.delete(f'post:{instance.slug}')
    cache.delete('posts:lista')

# Conectar signals no apps.py do app
# blog/apps.py
class BlogConfig(AppConfig):
    name = 'blog'

    def ready(self):
        import blog.signals  # noqa

Perguntas Frequentes

Django é só para APIs ou serve para sites completos?

Django serve para tudo: sites completos com templates HTML (Django Templates), APIs puras com Django REST Framework, e até aplicações em tempo real com Django Channels. É muito flexível.

Devo aprender Flask ou Django?

Para projetos sérios e que crescem, Django é a melhor escolha. Flask é mais simples e minimalista, ótimo para microserviços ou APIs pequenas. Nos tutoriais do canal, focamos em Django por ser mais completo para aplicações reais.

Django funciona bem com banco de dados PostgreSQL?

Sim, PostgreSQL é a combinação mais recomendada com Django em produção. O ORM do Django suporta nativamente PostgreSQL, MySQL, SQLite e Oracle. Com PostgreSQL você também tem acesso a campos especiais como JSONField, ArrayField e full-text search.

Preciso saber Python avançado para aprender Django?

Não precisa ser expert. Conhecimento intermediário de Python (funções, classes, módulos, decorators básicos) é suficiente para começar. O Django ensina boas práticas no processo.

Quais grandes sites usam Django?

Instagram, Pinterest, Disqus, Mozilla, National Geographic, Spotify (partes do backend) e muitos outros usam Django. É uma tecnologia testada em escala bilionária.

O que são as migrations do Django e por que são importantes?

Migrations são arquivos Python gerados automaticamente pelo Django que descrevem mudanças no schema do banco de dados. Elas permitem versionar o banco de dados junto com o código, fazer rollback e manter ambientes sincronizados. São essenciais para trabalho em equipe.

Como funciona o sistema de autenticação do Django?

Django vem com um sistema de autenticação completo: modelo User, login/logout, grupos, permissões e decorators como @login_required. Para autenticação social (Google, GitHub) ou APIs JWT, use o django-allauth com dj-rest-auth.

Qual a diferença entre Django e Django REST Framework?

Django é o framework base, completo para sites com templates HTML. O Django REST Framework (DRF) é uma biblioteca adicional que facilita a criação de APIs JSON. Para um backend que serve um frontend React ou mobile, você usa Django + DRF juntos.

Django suporta WebSockets e tempo real?

Sim, via Django Channels. É uma extensão oficial que adiciona suporte a WebSockets, permitindo aplicações em tempo real como chats, notificações ao vivo e dashboards. Usa ASGI em vez de WSGI.