Aula 76 – Loja Online – Tratando Erros
Aula 76 – Loja Online – Tratando Erros
Voltar para página principal do blog
Todas as aulas desse curso
Aula 75 Aula 77
Redes Sociais:
Meus links de afiliados:
Hostinger
Digital Ocean
One.com
Melhore seu NETWORKING
https://digitalinnovation.one/
Participe de comunidades de desenvolvedores:
Fiquem a vontade para me adicionar ao linkedin.
E também para me seguir no https://github.com/toticavalcanti.
Código final da aula:
https://github.com/toticavalcanti
Quer aprender python3 de graça e com certificado? Acesse então:
https://workover.com.br/python-codigo-fluente
Canais do Youtube
Toti
Backing Track / Play-Along
Código Fluente
Putz!
Vocal Techniques and Exercises
PIX para doações
Aula 76 – Loja Online – Tratando Erros
Quando inserimos ou removemos itens no carrinho, estamos fazendo requests jquery.
E se em produção, ocorrer algum problema?
Rodando localmente, no melhor dos cenários, vai funcionar sem problema, mas a aplicação rodando online, o tempo todo, pode ocorrer inumeras situações, que pode dá erro na request.
Precisamos informar ao usuário que ocorreu um problema.
Vamos emular um erro na função cart_update() do views.py do app cart.
e_commerce/carts/views.py
from django.http import JsonResponse
from django.shortcuts import render, redirect
from accounts.forms import LoginForm, GuestForm
from accounts.models import GuestEmail
from addresses.forms import AddressForm
from addresses.models import Address
from billing.models import BillingProfile
from orders.models import Order
from products.models import Product
from .models import Cart
def is_ajax(request):
return request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
def cart_detail_api_view(request):
cart_obj, new_obj = Cart.objects.new_or_get(request)
products = [{
"id": x.id,
"url": x.get_absolute_url(),
"name": x.title,
"price": x.price
} for x in cart_obj.products.all()]
# products_list = []
# for x in cart_obj.products.all():
# products_list.append({
# {"name": x.title, "price": x.price}
# })
cart_data = {"products": products, "subtotal": cart_obj.subtotal, "total": cart_obj.total}
return JsonResponse(cart_data)
def cart_home(request):
cart_obj, new_obj = Cart.objects.new_or_get(request)
return render(request, "carts/home.html", {"cart": cart_obj})
def cart_update(request):
product_id = request.POST.get('product_id')
if product_id is not None:
try:
product_obj = Product.objects.get(id=product_id)
except Product.DoesNotExist:
print("Mostrar mensagem ao usuário, esse produto acabou!")
return redirect("cart:home")
cart_obj, new_obj = Cart.objects.new_or_get(request)
if product_obj in cart_obj.products.all():
cart_obj.products.remove(product_obj)
added = False
else:
cart_obj.products.add(product_obj) # cart_obj.products.add(product_id)
added = True
request.session['cart_items'] = cart_obj.products.count()
# return redirect(product_obj.get_absolute_url())
if is_ajax(request):
print("Ajax request")
json_data = {
"added": added,
"removed": not added,
"cartItemCount": cart_obj.products.count()
}
#return JsonResponse(json_data)
return JsonResponse({"message":"Erro 400"}, status = 400)
return redirect("cart:home")
def checkout_home(request):
#aqui a gente pega o carrinho
cart_obj, cart_created = Cart.objects.new_or_get(request)
order_obj = None
#se o carrinho acabou de ser criado, ele tá zerado
#ou se o carrinho já existir mas não tiver nada dentro
if cart_created or cart_obj.products.count() == 0:
return redirect("cart:home")
login_form = LoginForm()
guest_form = GuestForm()
address_form = AddressForm()
billing_address_id = request.session.get("billing_address_id", None)
shipping_address_id = request.session.get("shipping_address_id", None)
billing_profile, billing_profile_created = BillingProfile.objects.new_or_get(request)
address_qs = None
if billing_profile is not None:
if request.user.is_authenticated:
address_qs = Address.objects.filter(billing_profile=billing_profile)
order_obj, order_obj_created = Order.objects.new_or_get(billing_profile, cart_obj)
if shipping_address_id:
order_obj.shipping_address = Address.objects.get(id = shipping_address_id)
del request.session["shipping_address_id"]
if billing_address_id:
order_obj.billing_address = Address.objects.get(id = billing_address_id)
del request.session["billing_address_id"]
if billing_address_id or shipping_address_id:
order_obj.save()
if request.method == "POST":
#verifica se o pedido foi feito
is_done = order_obj.check_done()
if is_done:
order_obj.mark_paid()
request.session['cart_items'] = 0
del request.session['cart_id']
return redirect("cart:success")
context = {
"object": order_obj,
"billing_profile": billing_profile,
"login_form": login_form,
"guest_form": guest_form,
"address_form": address_form,
"address_qs": address_qs,
}
return render(request, "carts/checkout.html", context)
def checkout_done_view(request):
return render(request, "carts/checkout-done.html", {})
No jquery
No jquery, vamos colocar algo do tipo: Ocorreu um erro, tente mais tarde novamente, no alert.
e_commerce/templates/base.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Base Template</title>
{% include 'base/css.html' %}
{% block base_head %}{% endblock base_head %}
</head>
<body>
{% include 'base/navbar.html' with nome_da_marca='Loja virtual' %}
<div class='container'>
{% block content %} {% endblock %}
</div>
{% include 'base/js.html' %}
<script>
$(document).ready(function(){
// Auto Search
const searchForm = $(".search-form")
const searchInput = searchForm.find("[name='q']") // input name='q'
const typingTimer = 0;
const typingInterval = 500 // .5 seconds
const searchBtn = searchForm.find("[type='submit']")
searchInput.keyup(function(event){
// key released
clearTimeout(typingTimer)
typingTimer = setTimeout(performSearch, typingInterval)
})
searchInput.keydown(function(event){
// key pressed
clearTimeout(typingTimer)
})
function displaySearching(){
searchBtn.addClass("disabled")
searchBtn.html("<i class='fa fa-spin fa-spinner'></i> Searching...")
}
function performSearch(){
displaySearching()
var query = searchInput.val()
setTimeout(function(){
window.location.href='/search/?q=' + query
}, 1000)
}
//Cart + Add Product
const productForm = $(".form-product-ajax")
productForm.submit(function(event){
event.preventDefault();
// console.log("O formulário não foi enviado!");
// o this pega os dados relacionados a esse form
const thisForm = $(this);
//const actionEndpoint = thisForm.attr("action");
const actionEndpoint = thisForm.attr("data-endpoint");
const httpMethod = thisForm.attr("method");
const formData = thisForm.serialize();
$.ajax({
url: actionEndpoint,
method: httpMethod,
data: formData,
success: function(data){
// console.log("Sucesso")
// console.log(data)
// console.log("Adicionado", data.added)
// console.log("Removido", data.removed)
const submitSpan = thisForm.find(".submit-span")
if(data.added){
submitSpan.html("No carrinho <button type='submit' class='btn btn-link'>Excluir</button>")
} else {
submitSpan.html("<button type='submit' class='btn btn-success'>Adicionar</button>")
}
const navbarCount = $(".navbar-cart-count")
navbarCount.text(data.cartItemCount)
const currentPath = window.location.href
if(currentPath.indexOf("cart") != -1){
refreshCart()
}
},
error: function(errorData){
alert("Ocorreu um erro, tente mais tarde novamente!")
console.log("Erro")
console.log(errorData)
}
})
})
function refreshCart(){
//console.log("Excluído do carrinho atual!")
const cartTable = $(".cart-table")
const cartBody = cartTable.find(".cart-body")
//cartBody.html("<h1>Mudou!</h1>")
const productsRow = cartBody.find(".cart-product")
const currentUrl = window.location.href
const refreshCartUrl = '/api/cart/';
const refreshCartMethod = "GET";
const data = {};
$.ajax({
url: refreshCartUrl,
method: refreshCartMethod,
data: data,
success: function(data){
console.log(data)
const hiddenCartItemRemoveForm = $(".cart-item-remove-form")
if(data.products.length > 0){
productsRow.html(" ")
let i = data.products.length
$.each(data.products, function(index, value){
const newCartItemRemove = hiddenCartItemRemoveForm.clone()
newCartItemRemove.css("display", "block")
newCartItemRemove.find(".cart-item-product-id").val(value.id)
cartBody.prepend("<tr><th scope=\"row\">" + i +
"</th><td><a href='" + value.url + "'>" +
value.name + "</a>" + newCartItemRemove.html() + "</td><td>" + value.price + "</td></tr>")
i--
})
cartBody.find(".cart-subtotal").text(data.subtotal)
cartBody.find(".cart-total").text(data.total)
} else {
window.location.href = currentUrl
}
},
error: function(errorData){
console.log("Erro")
console.log(errorData)
}
})
}
})
</script>
</body>
</html>
Vamos usar o jquery-confirm
Com o jquery-confirm, conseguiremos deixar o alert mais bonito e unificado em termos de experiência de usuário nos diferentes browsers.
Para usar, vamos colocar o css dele via cdn.
e_commerce/templates/base/css.html
{% load static %}
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<!--Font Awesome CSS-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"/>
<!--jquery-confirm css-->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-confirm/3.3.2/jquery-confirm.min.css">
E o javascript também, via cdn.
e_commerce/templates/base/js.html
{% load static %}
<!-- Optional JavaScript -->
<!-- jQuery first, Popper.js, Bootstrap JS and jquery-confirm JS -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-confirm/3.3.2/jquery-confirm.min.js"></script>
Para funcionar, é só colocar um .$ antes do alert()
e_commerce/templates/base.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Base Template</title>
{% include 'base/css.html' %}
{% block base_head %}{% endblock base_head %}
</head>
<body>
{% include 'base/navbar.html' with nome_da_marca='Loja virtual' %}
<div class='container'>
{% block content %} {% endblock %}
</div>
{% include 'base/js.html' %}
<script>
$(document).ready(function(){
// Auto Search
const searchForm = $(".search-form")
const searchInput = searchForm.find("[name='q']") // input name='q'
const typingTimer = 0;
const typingInterval = 500 // .5 seconds
const searchBtn = searchForm.find("[type='submit']")
searchInput.keyup(function(event){
// key released
clearTimeout(typingTimer)
typingTimer = setTimeout(performSearch, typingInterval)
})
searchInput.keydown(function(event){
// key pressed
clearTimeout(typingTimer)
})
function displaySearching(){
searchBtn.addClass("disabled")
searchBtn.html("<i class='fa fa-spin fa-spinner'></i> Searching...")
}
function performSearch(){
displaySearching()
var query = searchInput.val()
setTimeout(function(){
window.location.href='/search/?q=' + query
}, 1000)
}
//Cart + Add Product
const productForm = $(".form-product-ajax")
productForm.submit(function(event){
event.preventDefault();
// console.log("O formulário não foi enviado!");
// o this pega os dados relacionados a esse form
const thisForm = $(this);
//const actionEndpoint = thisForm.attr("action");
const actionEndpoint = thisForm.attr("data-endpoint");
const httpMethod = thisForm.attr("method");
const formData = thisForm.serialize();
$.ajax({
url: actionEndpoint,
method: httpMethod,
data: formData,
success: function(data){
// console.log("Sucesso")
// console.log(data)
// console.log("Adicionado", data.added)
// console.log("Removido", data.removed)
const submitSpan = thisForm.find(".submit-span")
if(data.added){
submitSpan.html("No carrinho <button type='submit' class='btn btn-link'>Excluir</button>")
} else {
submitSpan.html("<button type='submit' class='btn btn-success'>Adicionar</button>")
}
const navbarCount = $(".navbar-cart-count")
navbarCount.text(data.cartItemCount)
const currentPath = window.location.href
if(currentPath.indexOf("cart") != -1){
refreshCart()
}
},
error: function(errorData){
$.alert("Ocorreu um erro, tente mais tarde novamente!")
console.log("Erro")
console.log(errorData)
}
})
})
function refreshCart(){
//console.log("Excluído do carrinho atual!")
const cartTable = $(".cart-table")
const cartBody = cartTable.find(".cart-body")
//cartBody.html("<h1>Mudou!</h1>")
const productsRow = cartBody.find(".cart-product")
const currentUrl = window.location.href
const refreshCartUrl = '/api/cart/';
const refreshCartMethod = "GET";
const data = {};
$.ajax({
url: refreshCartUrl,
method: refreshCartMethod,
data: data,
success: function(data){
console.log(data)
const hiddenCartItemRemoveForm = $(".cart-item-remove-form")
if(data.products.length > 0){
productsRow.html(" ")
let i = data.products.length
$.each(data.products, function(index, value){
const newCartItemRemove = hiddenCartItemRemoveForm.clone()
newCartItemRemove.css("display", "block")
newCartItemRemove.find(".cart-item-product-id").val(value.id)
cartBody.prepend("<tr><th scope=\"row\">" + i +
"</th><td><a href='" + value.url + "'>" +
value.name + "</a>" + newCartItemRemove.html() + "</td><td>" + value.price + "</td></tr>")
i--
})
cartBody.find(".cart-subtotal").text(data.subtotal)
cartBody.find(".cart-total").text(data.total)
} else {
window.location.href = currentUrl
}
},
error: function(errorData){
console.log("Erro")
console.log(errorData)
}
})
}
})
</script>
</body>
</html>
Agora teste tentando modificar o carrinho e veja um alert mais padronizado e bonito.
Vamos usar outras opções que o jquery-confirm nos oferece, como tema, por exemplo..
e_commerce/templates/base.html
{% load static %}
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Base Template</title>
{% include 'base/css.html' %}
{% block base_head %}{% endblock base_head %}
</head>
<body>
{% include 'base/navbar.html' with nome_da_marca='Loja virtual' %}
<div class='container'>
{% block content %} {% endblock %}
</div>
{% include 'base/js.html' %}
<script>
$(document).ready(function(){
// Auto Search
const searchForm = $(".search-form")
const searchInput = searchForm.find("[name='q']") // input name='q'
const typingTimer = 0;
const typingInterval = 500 // .5 seconds
const searchBtn = searchForm.find("[type='submit']")
searchInput.keyup(function(event){
// key released
clearTimeout(typingTimer)
typingTimer = setTimeout(performSearch, typingInterval)
})
searchInput.keydown(function(event){
// key pressed
clearTimeout(typingTimer)
})
function displaySearching(){
searchBtn.addClass("disabled")
searchBtn.html("<i class='fa fa-spin fa-spinner'></i> Searching...")
}
function performSearch(){
displaySearching()
var query = searchInput.val()
setTimeout(function(){
window.location.href='/search/?q=' + query
}, 1000)
}
//Cart + Add Product
const productForm = $(".form-product-ajax")
productForm.submit(function(event){
event.preventDefault();
// console.log("O formulário não foi enviado!");
// o this pega os dados relacionados a esse form
const thisForm = $(this);
//const actionEndpoint = thisForm.attr("action");
const actionEndpoint = thisForm.attr("data-endpoint");
const httpMethod = thisForm.attr("method");
const formData = thisForm.serialize();
$.ajax({
url: actionEndpoint,
method: httpMethod,
data: formData,
success: function(data){
// console.log("Sucesso")
// console.log(data)
// console.log("Adicionado", data.added)
// console.log("Removido", data.removed)
const submitSpan = thisForm.find(".submit-span")
if(data.added){
submitSpan.html("No carrinho <button type='submit' class='btn btn-link'>Excluir</button>")
} else {
submitSpan.html("<button type='submit' class='btn btn-success'>Adicionar</button>")
}
const navbarCount = $(".navbar-cart-count")
navbarCount.text(data.cartItemCount)
const currentPath = window.location.href
if(currentPath.indexOf("cart") != -1){
refreshCart()
}
},
error: function(errorData){
$.alert({
title: "Oops!",
content: "Ocorreu um erro, tente mais tarde novamente!",
theme: "modern",
})
console.log("Erro")
console.log(errorData)
}
})
})
function refreshCart(){
//console.log("Excluído do carrinho atual!")
const cartTable = $(".cart-table")
const cartBody = cartTable.find(".cart-body")
//cartBody.html("<h1>Mudou!</h1>")
const productsRow = cartBody.find(".cart-product")
const currentUrl = window.location.href
const refreshCartUrl = '/api/cart/';
const refreshCartMethod = "GET";
const data = {};
$.ajax({
url: refreshCartUrl,
method: refreshCartMethod,
data: data,
success: function(data){
console.log(data)
const hiddenCartItemRemoveForm = $(".cart-item-remove-form")
if(data.products.length > 0){
productsRow.html(" ")
let i = data.products.length
$.each(data.products, function(index, value){
const newCartItemRemove = hiddenCartItemRemoveForm.clone()
newCartItemRemove.css("display", "block")
newCartItemRemove.find(".cart-item-product-id").val(value.id)
cartBody.prepend("<tr><th scope=\"row\">" + i +
"</th><td><a href='" + value.url + "'>" +
value.name + "</a>" + newCartItemRemove.html() + "</td><td>" + value.price + "</td></tr>")
i--
})
cartBody.find(".cart-subtotal").text(data.subtotal)
cartBody.find(".cart-total").text(data.total)
} else {
window.location.href = currentUrl
}
},
error: function(errorData){
$.alert({
title: "Oops!",
content: "Ocorreu um erro, tente mais tarde novamente!",
theme: "modern",
})
console.log("Erro")
console.log(errorData)
}
})
}
})
</script>
</body>
</html>