Aula 76 – Loja Online – Tratando Erros

Aula 76 – Loja Online – Tratando Erros

Loja Online - Django

Loja Online – Django

Pacote Programador Fullstack

Pacote Programador Fullstack

Voltar para página principal do blog

Todas as aulas desse curso

Aula 75                       Aula 77

Redes Sociais:

facebook

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

PIX Nubank

PIX Nubank


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>

Ficamos por aqui e até a próxima. 😉

Voltar para página principal do blog

Todas as aulas desse curso

Aula 75                       Aula 77

Código final da aula:

https://github.com/toticavalcanti

Canais do Youtube

Toti

Backing Track / Play-Along

Código Fluente

Putz!

Vocal Techniques and Exercises

Dêem um joinha 👍 na página do Código Fluente no
Facebook.

Sigam o Código Fluente no Instagram e no TikTok.

Código Fluente no Pinterest.

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Nos vemos na próxima então, \o/  😉 Bons Estudos!

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>