Python Django Fullstack explicado de forma simples (com diagramas e código real)
Python Django Fullstack: o essencial em um artigo — código real, diagramas e etapas concretas, extratos de um curso de 24 lições.
Um guia direto ao ponto: Python Django Fullstack dissecado com diagramas, exemplos concretos e comandos testados. Tudo vem de um curso estruturado de 8 capítulos — aqui está o melhor.
tl;dr
- Por que Django
- Models e migrações
- Views e URLs
- Templates e forms
- Admin
~$ cat ./parcours.md # Python Django Fullstack — 8 capítulos
01
Por que Django
→ Django vs Flask vs FastAPI→ Instalação e primeiro projeto+ 1 mais lições
02
Models e migrations
→ Models e fields→ Relations+ 1 mais lições
03
Views e URLs
→ Function-based views→ Class-based views (CBV)+ 1 mais lições
04
Templates e forms
→ Template language→ Forms e ModelForms+ 1 mais lições
05
Administração
→ Customizar ModelAdmin→ Actions e filters custom+ 1 mais lições
06
Autenticação
→ User model e auth views→ Custom User e Profile+ 1 mais lições
07
Implantação
→ Settings prod/dev→ Gunicorn + Nginx+ 1 mais lições
08
Projeto final blog ecom
→ Capítulo 07 – Projeto final : Models e admin→ Capítulo 07 – Projeto final : Views e templates+ 1 mais lições
🏁
Projeto final
→ Você sai com um projeto concreto e demonstrável
Migrações
Ciclo básico
# 1. Modificar um model class Article(models.Model): title = models.CharField(max_length=200) summary = models.TextField(blank=True) # NOVO # 2. Gerar a migração python manage.py makemigrations # Migrations for 'blog': # blog/migrations/0002_article_summary.py # 3. Aplicar python manage.py migrate # 4. Verificar python manage.py showmigrations
Ver o SQL gerado
python manage.py sqlmigrate blog 0002 # Output : # BEGIN; # ALTER TABLE blog_article ADD COLUMN summary TEXT NOT NULL DEFAULT ''; # COMMIT;
Reverter uma migração
python manage.py migrate blog 0001 # Reverter para 0001 (cancela 0002) python manage.py migrate blog zero # Cancelar tudo para o app blog
Migração de dados
python manage.py makemigrations --empty --name backfill_slugs blog # Editar o arquivo gerado : from django.db import migrations from django.utils.text import slugify def backfill_slugs(apps, schema_editor): Article = apps.get_model("blog", "Article") for a in Article.objects.all(): if not a.slug: a.slug = slugify(a.title) a.save() def reverse_func(apps, schema_editor): pass class Migration(migrations.Migration): dependencies = [("blog", "0001_initial")] operations = [migrations.RunPython(backfill_slugs, reverse_func)]
Compactando migrações
# Após muitas migrações, compactá-las : python manage.py squashmigrations blog 0001 0020 # Cria 0001_squashed_0020 que substitui os 20 arquivos # Novos desenvolvedores evitam executar 20 migrações
Migrações sem downtime
NOTEPara adicionar uma coluna NOT NULL sem quebrar a produção :
- Deploy 1 : Adicionar coluna nullable
- Deploy 2 : Backfill via migração de dados
- Deploy 3 : Modificar o código para usar/setter
- Deploy 4 : Migração ALTER NOT NULL
Renomear um model
# Se você renomear Article -> Post, Django vai propor # criar um novo model + excluir o antigo (perde os dados !) # Solução : editar a migração gerada operations = [ migrations.RenameModel("Article", "Post") ] # Idem para os campos operations = [ migrations.RenameField("Post", "summary", "excerpt") ]
Django vs Flask vs FastAPI
NOTEObjetivo — Compreender a filosofia Django e quando escolhê-lo.
Comparação rápida
| Critério | Django | Flask | FastAPI |
|---|---|---|---|
| Filosofia | Batteries included | Micro framework | API first, async |
| ORM | Integrado | SQLAlchemy externo | SQLAlchemy externo |
| Admin | Auto-gerado | A codificar | A codificar |
| Auth | Completo | Extensões | A codificar JWT |
| Templates | Django templates | Jinja2 | Jinja2 opcional |
| Async nativo | Parcial (4.x) | Não | Sim |
| Aprendizado | Mais longo | Muito simples | Médio |
Quando escolher Django
Quando NÃO escolher Django
Arquitetura MTV
+----------+ request +------+ query +-------+
| Browser | -----------> | View | --------> | Model |
+----------+ +------+ +-------+
^ | |
| v v
| +----------+ +-------+
+----------------- | Template |<--------| DB |
+----------+ +-------+
# M = Model (ORM, base de dados)
# T = Template (HTML)
# V = View (lógica de controle, em MVC é o Controller)Ecossistema Django
Versões e LTS
NOTEDjango releases :
- Django 5.0+ : atual
- Django 4.2 LTS : suportada até 2026
- Ciclo : nova versão a cada 8 meses
- LTS a cada 2 anos, suportadas por 3 anos
Empresas que usam Django
Resumo
NOTEPara lembrar
- Django = batteries included para apps web complexas
- Arquitetura MTV (Model, Template, View)
- Ideal : SaaS, CMS, e-commerce, dashboards
- API pura : prefira DRF ou FastAPI
Capítulo 07 – Projeto final : Modelos e admin
Caderno de encargos
NOTEObjetivo — Construir uma loja online minimalista com : catálogo, carrinho, pedidos, admin de negócio.
Estrutura do projeto
myshop/
manage.py
myshop/
settings/
urls.py
wsgi.py
accounts/
models.py # User custom
views.py
urls.py
catalog/
models.py # Category, Product
views.py
admin.py
cart/
cart.py # classe Cart (session)
views.py
orders/
models.py # Order, OrderItem
views.py
admin.py
templates/
static/
media/accounts/models.py
from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): phone = models.CharField(max_length=20, blank=True) address = models.TextField(blank=True) city = models.CharField(max_length=100, blank=True) postal_code = models.CharField(max_length=10, blank=True) country = models.CharField(max_length=2, default="CA") class Meta: ordering = ["-date_joined"] def __str__(self): return self.username
catalog/models.py
from django.db import models from django.urls import reverse from django.utils.text import slugify class Category(models.Model): name = models.CharField(max_length=100, unique=True) slug = models.SlugField(max_length=120, unique=True) description = models.TextField(blank=True) parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE, related_name="children") class Meta: verbose_name_plural = "Categories" ordering = ["name"] def __str__(self): return self.name def get_absolute_url(self): return reverse("catalog:category", kwargs={"slug": self.slug}) class Product(models.Model): category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name="products") name = models.CharField(max_length=200) slug = models.SlugField(max_length=220, unique=True) description = models.TextField() price = models.DecimalField(max_digits=10, decimal_places=2) image = models.ImageField(upload_to="products/%Y/%m/") stock = models.PositiveIntegerField(default=0) available = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ["-created_at"] indexes = [ models.Index(fields=["slug"]), models.Index(fields=["available", "-created_at"]), ] def __str__(self): return self.name def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.name) super().save(*args, **kwargs) def get_absolute_url(self): return reverse("catalog:product", kwargs={"slug": self.slug}) @property def in_stock(self): return self.available and self.stock > 0
orders/models.py
class Order(models.Model): class Status(models.TextChoices): PENDING = "pending", "Em espera" PAID = "paid", "Pago" SHIPPED = "shipped", "Enviado" DELIVERED = "delivered", "Entregue" CANCELED = "canceled", "Cancelado" user = models.ForeignKey("accounts.User", on_delete=models.PROTECT, related_name="orders") status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING) address = models.TextField() city = models.CharField(max_length=100) postal_code = models.CharField(max_length=10) country = models.CharField(max_length=2) total = models.DecimalField(max_digits=12, decimal_places=2, default=0) paid_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ["-created_at"] def __str__(self): return f"Order #{self.id}" class OrderItem(models.Model): order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="items") product = models.ForeignKey("catalog.Product", on_delete=models.PROTECT) price = models.DecimalField(max_digits=10, decimal_places=2) quantity = models.PositiveIntegerField(default=1) @property def subtotal(self): return self.price * self.quantity
catalog/admin.py
@admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ["name", "parent", "product_count"] prepopulated_fields = {"slug": ("name",)} search_fields = ["name"] def product_count(self, obj): return obj.products.count() @admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display = ["name", "category", "price", "stock", "available"] list_filter = ["available", "category", "created_at"] list_editable = ["price", "stock", "available"] search_fields = ["name", "description"] prepopulated_fields = {"slug": ("name",)} list_per_page = 50
orders/admin.py
class OrderItemInline(admin.TabularInline): model = OrderItem extra = 0 readonly_fields = ["product", "price", "quantity", "subtotal"] can_delete = False @admin.register(Order) class OrderAdmin(admin.ModelAdmin): list_display = ["id", "user", "status", "total", "created_at", "paid_at"] list_filter = ["status", "created_at"] search_fields = ["id", "user__username", "user__email"] inlines = [OrderItemInline] actions = ["mark_shipped"] date_hierarchy = "created_at" @admin.action(description="Marcar como enviado") def mark_shipped(self, request, queryset): queryset.update(status=Order.Status.SHIPPED)
va-plus-loin
Este artigo cobre os trechos mais úteis — o curso completo Python Django Fullstack (8 capítulos, 24 lições, exercícios corrigidos e projeto final) leva você até o fim.
./acceder-au-cours-complet curso gratuito : Vibe CodingFAQ
Quanto tempo para aprender Python Django Fullstack ?
Com uma progressão estruturada (8 capítulos, 24 lições curtas e práticas), você atinge um nível operacional em algumas semanas dedicando 30 a 60 minutos por dia. O importante é praticar cada conceito imediatamente.
Precisa de pré-requisitos ?
Básicos de informática são suficientes. Se você sabe usar um terminal e ler código simples, está pronto.
Por onde começar concretamente ?
Reproduza os comandos deste artigo, depois siga o curso completo Python Django Fullstack : ele encadeia as 24 lições em ordem, com exercícios e projeto final.
./a-lire-aussi
→ Lance-se em Portfolio IA SEO Vercel : seu primeiro passo concreto hoje→ IA Stripe GitHub SaaS na prática : o código e os comandos que realmente importam→ Python Requests APIs explicado de forma simples (com diagramas e código real)📬 Quer receber este tipo de guia toda semana ? Inscreva-se gratuitamente — código real, zero enrolação.