Aula 98 – Django – Ecommerce – Baixa Automática de Estoque
Aula 98 – Django – Ecommerce – Baixa Automática de Estoque

Loja Online – Django
Voltar para página principal do blog
Todas as aulas desse curso
Aula 97 Aula 99
Redes Sociais do Código Fluente:
Scarlett Finch
Scarlett Finch é uma influenciadora virtual criada com IA.
Ela é 🎤 cantora e 🎶compositora pop britânica.
Siga a Scarlett Finch no Instagram:
Conecte-se comigo!
LinkedIn: Fique à vontade para me adicionar no LinkedIn.
Ao conectar-se comigo, você terá acesso a atualizações regulares sobre desenvolvimento web, insights profissionais e oportunidades de networking no setor de tecnologia.
GitHub: Siga-me no GitHub para ficar por dentro dos meus projetos mais recentes, colaborar em código aberto ou simplesmente explorar os repositórios que eu contribuo, o que pode ajudar você a aprender mais sobre programação e desenvolvimento de software.
Recursos e Afiliados
Explorando os recursos abaixo, você ajuda a apoiar nosso site.
Somos parceiros afiliados das seguintes plataformas:
- https://heygen.com/ – Eleve a produção de seus vídeos com HeyGen! Com esta plataforma inovadora, você pode criar vídeos envolventes utilizando avatares personalizados, ideal para quem busca impactar e conectar com audiências em todo o mundo. HeyGen transforma a maneira como você cria conteúdo, oferecendo ferramentas fáceis de usar para produzir vídeos educativos, demonstrações de produtos e muito mais. Descubra o poder de comunicar através de avatares interativos e traga uma nova dimensão para seus projetos. Experimente HeyGen agora e revolucione sua forma de criar vídeos!
- letsrecast.ai – Redefina a maneira como você consome artigos com Recast. Esta plataforma transforma artigos longos em diálogos de áudio que são informativos, divertidos e fáceis de entender. Ideal para quem está sempre em movimento ou busca uma forma mais conveniente de se manter informado. Experimente Recast agora.
- dupdub.com – Explore o universo do marketing digital com DupDub. Esta plataforma oferece ferramentas inovadoras e soluções personalizadas para elevar a sua estratégia de marketing online. Ideal para empresas que buscam aumentar sua visibilidade e eficiência em campanhas digitais. Descubra mais sobre DupDub.
- DeepBrain AI Studios – Revolucione a criação de conteúdo com a tecnologia de inteligência artificial da DeepBrain AI Studios. Esta plataforma avançada permite que você crie vídeos interativos e apresentações utilizando avatares digitais gerados por IA, que podem simular conversas reais e interações humanas. Perfeito para educadores, criadores de conteúdo e empresas que querem inovar em suas comunicações digitais. Explore DeepBrain AI Studios.
- Audyo.ai – Transforme a maneira como você interage com conteúdo auditivo com Audyo.ai. Esta plataforma inovadora utiliza inteligência artificial para criar experiências de áudio personalizadas, melhorando a acessibilidade e a compreensão de informações através de podcasts, transcrições automáticas e síntese de voz avançada. Ideal para profissionais de mídia, educadores e qualquer pessoa que deseje acessar informações auditivas de maneira mais eficiente e envolvente. Descubra Audyo.ai e suas possibilidades.
- Acoust.io – Transforme sua produção de áudio com Acoust.io. Esta plataforma inovadora fornece uma suite completa de ferramentas para criação, edição e distribuição de áudio, ideal para artistas, produtores e empresas de mídia em busca de excelência e inovação sonora. Acoust.io simplifica o processo de levar suas ideias à realidade, oferecendo soluções de alta qualidade que elevam seus projetos de áudio. Experimente Acoust.io agora e descubra um novo patamar de possibilidades para seu conteúdo sonoro.
- Hostinger – Hospedagem web acessível e confiável. Ideal para quem busca soluções de hospedagem de sites com excelente custo-benefício e suporte ao cliente robusto. Saiba mais sobre a Hostinger.
- Digital Ocean – Infraestrutura de nuvem para desenvolvedores. Oferece uma plataforma de nuvem confiável e escalável projetada especificamente para desenvolvedores que precisam de servidores virtuais, armazenamento e networking. Explore a Digital Ocean.
- One.com – Soluções simples e poderosas para o seu site. Uma escolha ideal para quem busca registrar domínios, hospedar sites ou criar presença online com facilidade e eficiência. Visite One.com.
Educação e Networking
Amplie suas habilidades e sua rede participando de cursos gratuitos e comunidades de desenvolvedores:
- Digital Innovation One – Cursos gratuitos com certificado.
- Workover – Aprenda Python3 gratuitamente.
- Comunidades de desenvolvedores para networking:
Canais do Youtube
Explore nossos canais no YouTube para uma variedade de conteúdos educativos e de entretenimento, cada um com um foco único para enriquecer sua experiência de aprendizado e lazer.
Toti
Toti: Meu canal pessoal, onde posto clips artesanais de músicas que curto tocar, dicas de teoria musical, entre outras coisas.
Scarlett Finch
Scarlett Finch: Cantora e influenciadora criada com IA.
Lofi Music Zone Beats
Lofi Music Zone Beats: O melhor da música Lofi para estudo, trabalho e relaxamento, criando o ambiente perfeito para sua concentração.
Backing Track / Play-Along
Backing Track / Play-Along: Acompanhe faixas instrumentais para prática musical, ideal para músicos que desejam aprimorar suas habilidades.
Código Fluente
Código Fluente: Aulas gratuitas de programação, devops, IA, entre outras coisas.
Putz!
Putz!: Canal da banda Putz!, uma banda virtual, criada durante a pandemia com mais 3 amigos, Fábio, Tatá e Lula.
PIX para doações

PIX Nubank
Aula 98 – Django – Ecommerce – Baixa Automática de Estoque
Código da aula: Github
Crie a branch feature/stock-management e entre nela
git checkout -b feature/stock-managementO que vamos fazer nessa aula?
Na Aula 96 adicionamos o campo stock ao modelo Product e o modelo CartProduct passou a registrar a quantity de cada item no carrinho. Na Aula 97 fechamos o ciclo de pagamento com cartões salvos.
O que falta agora é a consequência natural de uma venda: quando o pagamento é confirmado, o estoque precisa cair. Hoje isso não acontece — você pode vender o mesmo produto infinitas vezes sem que o número de unidades disponíveis se mova.
Nesta aula vamos fechar essa lacuna em três frentes:
- Baixa automática de estoque quando o pagamento é confirmado
- Proteção contra overselling na view de adicionar ao carrinho
- Botão “Adicionar” desabilitado na listagem quando o estoque for zero
Boas práticas no Github
Crie a branch da aula, vou chamar a minha de: feature/stock-management
Usar feature/… para branches no GitHub ajuda a organizar e identificar rapidamente branches dedicadas ao desenvolvimento de novas funcionalidades.
Prefixos como feature/, bugfix/, hotfix/ e release/ são amplamente aceitos como boas práticas na comunidade de desenvolvimento e ajudam a manter um fluxo de trabalho consistente e organizado.
Atualizações no código
Parte 1 – Baixa de Estoque Pós-Pagamento
Explicação do Código – payment_success_view – e_commerce/billing/views.py
Por que essa função é o lugar certo para a baixa de estoque?
A payment_success_view já é o ponto de chegada depois que o Stripe confirma o pagamento. Ela já chama order_obj.mark_paid() e limpa a sessão. Só precisamos acrescentar a lógica de subtração de estoque nesse mesmo momento — sem criar nenhum arquivo novo.
O que mudou:
- Importação adicionada:
from django.db import transaction - Bloco de baixa de estoque inserido dentro do
if order_obj— itera sobrecartproduct_sete subtrai aquantitydostockde cada produto - Uso de
transaction.atomic()garante que todas as baixas acontecem juntas ou nenhuma acontece — protegendo a consistência do banco
Funcionamento linha por linha:
1. transaction.atomic()
Abre uma transação de banco de dados. Se qualquer produto falhar ao salvar, o Django faz rollback e nenhuma baixa é registrada. Isso evita que o estoque fique pela metade em caso de erro.
2. cart_obj.cartproduct_set.all()
Recupera todos os CartProducts do carrinho — cada um com seu product e sua quantity.
3. product.stock = max(0, product.stock – cart_product.quantity)
Subtrai a quantidade comprada do estoque disponível. O max(0, ...) é uma proteção extra: garante que o estoque nunca fique negativo, mesmo em situações de corrida entre duas compras simultâneas.
4. product.save()
Persiste a atualização no banco de dados.
e_commerce/billing/views.py
from django.db import transaction
# ... demais imports inalterados ...
def payment_success_view(request):
cart_id = request.session.get("cart_id")
if cart_id:
cart_obj = Cart.objects.get(id=cart_id)
order_obj = Order.objects.filter(cart=cart_obj).first()
if order_obj:
order_obj.mark_paid()
# NOVO – baixa de estoque após pagamento confirmado
with transaction.atomic():
for cart_product in cart_obj.cartproduct_set.all():
product = cart_product.product
product.stock = max(0, product.stock - cart_product.quantity)
product.save()
request.session['cart_items'] = 0
del request.session['cart_id']
return render(request, 'billing/payment-success.html')Parte 2 – Proteção Contra Overselling
Explicação do Código – add_to_cart – e_commerce/carts/views.py
O que é overselling?
Overselling acontece quando um cliente consegue comprar mais unidades do que existem em estoque. O campo max no input HTML da listagem já limita o que o usuário pode digitar na interface. Mas qualquer pessoa com acesso ao DevTools do navegador — ou enviando uma requisição direta via curl ou Postman — consegue ignorar esse limite e enviar a quantidade que quiser.
A proteção real precisa estar no backend.
O que mudou:
- Verificação de estoque disponível antes de aceitar o item no carrinho
- Cálculo da quantidade já existente no carrinho para o mesmo produto, evitando que o total ultrapasse o
stock - Resposta JSON com mensagem de erro amigável quando o estoque é insuficiente
Funcionamento linha por linha:
1. Verificação de produto com estoque zero
Antes de qualquer operação no carrinho, checamos se product.stock > 0. Se o produto já esgotou, retornamos erro imediatamente.
2. Quantidade já no carrinho
Um cliente pode adicionar o mesmo produto em momentos diferentes. Precisamos somar o que ele já tem no carrinho com o que está tentando adicionar agora, e comparar esse total com o estoque disponível.
3. Resposta de erro padronizada
O JSON de retorno segue o mesmo padrão já usado na view — success: False e uma mensagem legível para o frontend exibir ao usuário.
e_commerce/carts/views.py
def add_to_cart(request):
if request.method == "POST":
product_id = request.POST.get("product_id")
quantity = request.POST.get("quantity", 1)
try:
quantity = int(quantity)
except ValueError:
return JsonResponse({"success": False, "message": "Quantidade inválida."})
product = get_object_or_404(Product, id=product_id)
# NOVO – proteção contra overselling
if product.stock <= 0:
return JsonResponse({
"success": False,
"message": "Produto esgotado."
})
cart_obj, created = Cart.objects.new_or_get(request)
# NOVO – verifica quantidade já existente no carrinho
cart_product = CartProduct.objects.filter(
cart=cart_obj, product=product
).first()
quantity_in_cart = cart_product.quantity if cart_product else 0
# NOVO – impede que o total ultrapasse o estoque
if quantity_in_cart + quantity > product.stock:
return JsonResponse({
"success": False,
"message": f"Estoque insuficiente. Disponível: {product.stock - quantity_in_cart} unidade(s)."
})
cart_product_obj, created = CartProduct.objects.get_or_create(
cart=cart_obj, product=product
)
cart_product_obj.quantity = cart_product_obj.quantity + quantity if not created else quantity
cart_product_obj.save()
cart_obj.update_totals()
request.session["cart_id"] = cart_obj.id
request.session["cart_items"] = cart_obj.cartproduct_set.count()
return JsonResponse({"success": True, "cart_items": cart_obj.cartproduct_set.count()})
return JsonResponse({"success": False, "message": "Método não permitido."})Parte 3 – Botão Desabilitado Quando Sem Estoque
Explicação do Código – card.html – e_commerce/products/templates/products/snippets/card.html
Por que precisamos mudar o template?
O campo max="{{ instance.stock }}" no input de quantidade já estava correto — ele impede que o usuário escolha mais unidades do que existe. Mas quando o estoque é zero, o formulário ainda aparece normalmente e o botão “Adicionar” continua clicável. O usuário tenta adicionar, o backend rejeita, mas a experiência é ruim.
A correção é simples: quando stock == 0, escondemos o formulário e exibimos uma mensagem de esgotado no lugar.
O que mudou:
- Condicional
{% if instance.stock > 0 %}envolve todo o formulário de adicionar ao carrinho - Bloco
{% else %}exibe badge “Esgotado” quandostocké zero - Nenhuma alteração nos campos existentes — apenas o wrapper condicional foi adicionado
e_commerce/products/templates/products/snippets/card.html
<!-- Path: templates/products/snippets/card.html -->
{% load static %}
<div class="card h-100 shadow-sm">
<div class="card-img-wrapper position-relative">
{% if instance.images.all %}
<a href="{{ instance.get_absolute_url }}">
<img src="{{ instance.images.first.image.url }}" class="card-img-top rounded" alt="{{ instance.images.first.alt_text|default:instance.title }}">
</a>
{% else %}
<img src="{% static 'placeholder.jpg' %}" class="card-img-top rounded" alt="Placeholder">
{% endif %}
{% if instance.discount_price %}
<span class="badge bg-danger position-absolute top-0 start-0 m-2">Promoção</span>
{% endif %}
</div>
<div class="card-body d-flex flex-column">
<h5 class="card-title text-center text-uppercase fw-bold">{{ instance.title }}</h5>
<p class="card-text text-muted text-center flex-grow-1">{{ instance.description|truncatewords:14 }}</p>
{% if instance.discount_price %}
<p class="card-text text-center">
<span class="text-muted text-decoration-line-through">R$ {{ instance.price }}</span>
<span class="text-danger fw-bold">R$ {{ instance.discount_price }}</span>
</p>
{% else %}
<p class="card-text text-center fw-bold">R$ {{ instance.price }}</p>
{% endif %}
</div>
<div class="card-footer bg-light border-0">
{% if instance.stock > 0 %} {# NOVO #}
<form
method="POST"
action="{% url 'cart:update' %}"
class="d-flex align-items-center justify-content-between form-product-ajax"
>
{% csrf_token %}
<input type="hidden" name="product_id" value="{{ instance.id }}">
<!-- Campo de Quantidade -->
<div class="me-2">
<input
type="number"
name="quantity"
value="1"
min="1"
max="{{ instance.stock }}"
class="form-control form-control-sm text-center"
style="width: 60px;"
>
</div>
<!-- Botão Adicionar -->
<button
type="submit"
class="btn btn-primary btn-sm"
style="padding: 0.375rem 0.75rem; font-size: 14px;"
>
Adicionar
</button>
</form>
{% else %} {# NOVO #}
<div class="text-center">
<span class="badge bg-secondary w-100 py-2">Esgotado</span>
</div>
{% endif %} {# NOVO #}
</div>
</div>Testando a Implementação
Stripe
Certifique-se que está logado no Stripe
Faça login com sua conta Stripe
stripe loginEncaminhe eventos ao seu webhook
stripe listen --forward-to localhost:8000/api/webhookPronto! O projeto está configurado, o banco de dados está populado, o Stripe já tá ouvindo os eventos em modo teste e você pode acompanhar a aula sem problemas e fazer todos os testes.
Fluxo de Teste
- No Django Admin, defina
stock = 2para um produto de teste - Adicione 2 unidades desse produto ao carrinho e conclua o pagamento
- Após o pagamento, verifique no Admin que
stockcaiu para 0 - Tente adicionar o produto novamente — o backend deve retornar “Produto esgotado”
- Na listagem de produtos, o botão “Adicionar” deve ter sido substituído pelo badge “Esgotado”
- Tente enviar manualmente uma requisição POST para
/cart/add/comquantitymaior que o estoque via DevTools ou Postman — o backend deve rejeitar com mensagem de estoque insuficiente
Cartões de Teste do Stripe
Visa: 4242 4242 4242 4242
Mastercard: 5555 5555 5555 4444
American Express: 3782 822463 10005
Discover: 6011 1111 1111 1117
CVV: Qualquer 3 dígitos (4 para Amex)
Data: Qualquer data futura
CEP: Qualquer CEP válidoResumo da Aula
Nesta aula fechamos a regra de negócio de estoque que estava engatilhada desde a Aula 96:
- ✅ Baixa automática de estoque em
billing/views.py— comtransaction.atomic()para garantir consistência - ✅ Proteção contra overselling em
carts/views.py— valida no backend independente do frontend - ✅ Badge “Esgotado” em
card.html— esconde o formulário quandostocké zero
🎯 Resultado: O e-commerce agora tem controle de estoque real. Cada venda confirmada desconta automaticamente as unidades disponíveis, e ninguém consegue comprar o que não existe em estoque.
🚀 Próxima Aula: Categorias Dinâmicas na Navbar
Na Aula 96 construímos todo o app categories — modelo, views e URLs.
A rota category/<slug:slug>/ já está configurada e funcionando.
Mas se você olhar a navbar agora, os links ainda são fixos: Home, Contato e Produtos.
📋 O que você vai aprender na Aula 99:
1. Context Processors no Django
Como injetar dados em todos os templates sem modificar cada view individualmente.
2. Dropdown de Categorias na Navbar
Puxando todas as categorias ativas do banco automaticamente, sem hardcode.
3. Filtragem de Produtos por Categoria
Integrando a URL que já existe com a navegação do usuário na interface.
💡 Resultado: O cliente poderá navegar pela loja por categoria a partir de qualquer página — exatamente como nos grandes e-commerces do mercado.
🗺️ O Plano Completo até o Fim do Módulo
A Aula 98 fechou uma regra de negócio importante, mas ainda temos bastante chão para cobrir antes de colocar essa loja de pé de verdade. Aqui está o que vem pela frente:
- Aula 99 — Categorias dinâmicas na navbar (context processor + dropdown)
- Aula 100 — Verificação de e-mail no cadastro (o campo
is_verifiedjá existe no modelo, falta o fluxo completo) - Aula 101 — Dashboard do cliente: “Meus Pedidos” com histórico e status
- Aula 102 — Webhook do Stripe (confirmação de pagamento no backend, independente do browser)
- Aula 103 — Cupons de desconto aplicados no carrinho
- Aula 104 — E-mails transacionais: pedido confirmado, boas-vindas
- Aula 105 — Deploy com Docker + Digital Ocean
Depois que esse ciclo fechar, o Django estará completo como backend de um e-commerce real.
E aí começa a Fase B.
O frontend que construímos aqui cumpriu o papel dele — mas o mercado hoje trabalha diferente. No próximo módulo vamos reescrever toda a camada de apresentação com Next.js, consumindo o Django via Django REST Framework. O backend que você já domina continua valendo. O que muda é como o cliente enxerga a loja.
Isso vai virar um módulo separado, com identidade própria — e você vai sair dele sabendo construir uma stack completa, do banco de dados ao frontend moderno.
Por enquanto, nos vemos na Aula 99. 👊.



