Desarrollo de Ecommerce con Django (parte 3)

Gabriel Villacis - Apr 1 - - Dev Community

Este tutorial cubre los siguientes puntos:

  • Layout de la web.
  • Herencia de plantillas.
  • Formularios de registro de usuario e inicio de sesión.
  • Flujo de inicio y cierre de sesión.

1. Reestructurar el layout de la plantilla principal

Vamos a estructurar el layout para que tenga una zona de header, nav, main y footer.

Usemos el siguiente código que está basado en los ejemplos de bootstrap 5:

{% load static %}
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Django Ecommerce</title>
    <link rel="shortcut icon" href="{% static 'img/favicon.ico' %}" type="image/x-icon">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
    <div class="container-fluid">
      <!-- Header -->
      <header class="d-flex flex-wrap align-items-center justify-content-center justify-content-md-between py-3 mb-4 border-bottom">
          <ul class="nav col-12 col-md-auto mb-2 justify-content-center mb-md-0">
            <li><a href="#" class="nav-link px-2 link-secondary">Home</a></li>
            <li><a href="#" class="nav-link px-2 link-dark">Quiénes somos</a></li>
            <li><a href="#" class="nav-link px-2 link-dark">FAQs</a></li>              
          </ul>

          <div class="col-md-3 text-end">
            {% if user.is_authenticated %}
              <a href="#" class="nav-link link-body-emphasis px-2">Bienvenido, {{user.username}}</a>
              <a href="#" class="nav-link link-body-emphasis px-2">Cerrar sesión</a>
            {% else %}
              <a href="#" class="btn btn-outline-primary me-2">Iniciar sesión</a>
              <a href="#" class="btn btn-primary">Registrarse</a>
            {% endif %}
          </div>
      </header>

      <!-- Navigation -->
      <nav class="navbar navbar-expand-lg bg-body-tertiary">
          <div class="container-fluid">
            <div class="collapse navbar-collapse" id="navbarSupportedContent">
              <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                  <li class="nav-item dropdown">
                      <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                    Categorías
                  </a>
                  <ul class="dropdown-menu">
                    <li><a class="dropdown-item" href="#">Todas las categorías</a></li>                      
                  </ul>
                </li>
              </ul>              
            </div>
          </div>
      </nav>

      <!-- Main -->
      <main class="container my-5">
          <h1 class="text-center">Catálogo de Productos</h1>
          <div class="my-3 row row-cols-1 row-cols-md-3 g-4">
            {% for producto in productos %}
              <div class="col">
                <div class="card h-100">
                  <img src="{{producto.imagen.url}}" class="card-img-top" alt="...">
                  <div class="card-body">
                    <h5 class="card-title">{{producto.nombre}}</h5>
                    <p class="card-text">{{producto.descripcion}}</p>                    
                  </div>
                  <div class="card-footer">
                    <div class="d-flex justify-content-between">
                      <span class="badge rounded-pill text-bg-light fs-6">${{producto.precio}}</span>
                      <button class="btn btn-sm btn-primary" {% if not user.is_authenticated %} disabled {% endif %}>Hacer pedido</button>
                  </div>
                  </div>
                </div>
              </div> 
            {% endfor %}             
          </div>
      </main>

      <!-- Footer-->
      <footer class="py-5 bg-primary mb-2">
        <div class="container">
          <p class="text-center text-white">Copyright &copy; Gabriel Villacis 2024</p>
        </div>
      </footer>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

2. Herencia de plantillas
Para realizar la herencia de plantillas vamos a incorporar en la plantilla base la definición de un bloque. Por ejemplo:

En la plantilla index.html declaramos:

<main class="container my-5">
  {% block contenido %}
  {% endblock %}
</main>
Enter fullscreen mode Exit fullscreen mode

Y creamos una nueva plantilla catalog.html la cual extenderá index.html y definirá el bloque contenido:

{% extends 'index.html' %}

{% block contenido %}  
  <!-- Aquí va el código para presentar el detalle del catálogo -->
  <h1 class="text-center">Catálogo de Productos</h1>
  <div class="my-3 row row-cols-1 row-cols-md-3 g-4">
  {% for producto in productos %}
    ... 
    ... 
    ... 
  {% endblock %}
{% endfor %}
Enter fullscreen mode Exit fullscreen mode

Paso 3: Creación del formulario de registro de usuario

  • Crear la plantilla HTML para el formulario de registro:

Necesitamos crear una plantilla HTML para renderizar el formulario. Crea el archivo signup.html en el directorio de plantillas de la aplicación "store" y agrega el siguiente código:

{% load static %}
<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Registro de Usuario</title>
    <link rel="shortcut icon" href="{% static 'img/favicon.ico' %}" type="image/x-icon">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">    
</head>
<body>
    <div class="container mt-5">
        <div class="row h-100 justify-content-center align-items-center">        
            <div class="col-md-8">
                <h3 class="text-center">Registro de Usuario</h3>                
                <form id="signupForm" class="row g-3">
                    <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
                    <div class="col-md-6">
                        <label for="firstNameInput" class="form-label">Nombre:</label>                
                        <input type="text" class="form-control" id="firstNameInput" name="first_name" required>
                    </div>
                    <div class="col-md-6">
                        <label for="lastNameInput" class="form-label">Apellido:</label>                
                        <input type="text" class="form-control" id="lastNameInput" name="last_name" required>
                    </div>                    
                    <div class="col-md-6">
                        <label for="emailInput" class="form-label">Email:</label>
                        <input type="email" class="form-control" id="emailInput" name="email">
                    </div>
                    <div class="col-md-6">
                        <label for="usernameInput" class="form-label">Usuario:</label>                
                        <input type="text" class="form-control" id="usernameInput" name="username" required>
                    </div>  
                    <div class="col-md-6">
                        <label for="password1Input" class="form-label">Contraseña:</label>                
                        <input type="password" class="form-control" id="password1Input" name="password1" required>
                    </div> 
                    <div class="col-md-6">
                        <label for="password2Input" class="form-label">Repita la contraseña:</label>                
                        <input type="password" class="form-control" id="password2Input" name="password2" required>
                    </div>
                    <div class="col-md-12">
                        <button type="submit" class="btn btn-primary">Registrar</button>
                        <button type="reset" class="btn btn-secondary">Cancelar</button>
                        <a href="{% url 'home' %}" class="btn btn-link">Ir a la Tienda</a>
                    </div>
                </form>
            </div>
        </div>        
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • Configurar la vista:

Ahora, necesitamos crear una vista en Django para procesar el formulario. Puedes agregar el siguiente código a tu archivo views.py:

...
...

from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.contrib.auth.hashers import make_password
from datetime import datetime
from django.http import JsonResponse
import json

def do_signup(request):
    if request.method == 'POST':
        data = json.loads(request.body)  
        first_name = data['first_name']
        last_name = data['last_name']
        email = data['email']
        username = data['username']
        password = data['password1']

        if User.objects.filter(username=username).exists():            
            return JsonResponse({'success': False, 'message': 'El username ingresado ya está siendo utilizado.'}, status=400)
        else:            
            nuevo_usuario = User(
                username=username,
                password=make_password(password),
                is_superuser=False,
                first_name=first_name,
                last_name=last_name,
                email=email,
                is_staff=False,
                is_active=True,
                date_joined=datetime.now()
            )
            nuevo_usuario.save()
            return JsonResponse({'success': True, 'message': 'El usuario se registró exitosamente.'}, status=200)
    else:        
        return render(request, 'signup.html')
Enter fullscreen mode Exit fullscreen mode
  • Agregar la URL en el archivo de URLs de la aplicación:

Ahora necesitamos configurar una URL para nuestra vista. Agrega el siguiente código a tu archivo urls.py de la aplicación "store":

from django.urls import path
from . import views

urlpatterns = [
  # Otras rutas
  path('signup/', views.do_signup, name='signup')
]
Enter fullscreen mode Exit fullscreen mode

A continuación, se debe crear la respectiva función de JavaScript para consumir el método POST de creación del usuario.

Para probar el flujo de registro completo, asegúrate de que tus rutas y vistas estén configuradas correctamente y ejecuta tu servidor Django. Luego, accede a la URL http://localhost:8000/signup en tu navegador para iniciar el proceso de registro.

Paso 4: Creación del formulario de inicio de sesión

A continuación, vamos a crear el flujo de inicio de sesión:

  • Crear la plantilla HTML para el formulario de Inicio de sesión:

Ahora, necesitamos crear una plantilla HTML para renderizar el formulario de inicio de sesión. Crea el archivo signin.html en tu directorio de plantillas y agregar el siguiente código:

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Inicio de Sesión</title>
    <link rel="shortcut icon" href="{% static 'img/favicon.ico' %}" type="image/x-icon">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
    <div class="container mt-5">
        <div class="row h-100 justify-content-center align-items-center">        
            <div class="col-md-8">
                <h3 class="text-center">Inicio de Sesión</h3>
                <form id="signinForm" class="row g-3">
                    <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
                    <div class="col-md-6">
                        <label for="usernameInput" class="form-label">Usuario:</label>                
                        <input type="text" class="form-control" id="usernameInput" name="username" required>
                    </div>  
                    <div class="col-md-6">
                        <label for="passwordInput" class="form-label">Contraseña:</label>                
                        <input type="password" class="form-control" id="passwordInput" name="password" required>
                    </div>                     
                    <div class="col-md-12">
                        <button type="submit" class="btn btn-primary">Iniciar Sesión</button>
                        <button type="reset" class="btn btn-secondary">Cancelar</button>
                        <a href="{% url 'home' %}" class="btn btn-link">Ir a la Tienda</a>
                    </div>
                </form>
            </div>
        </div>        
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • Definir la vista de Inicio de Sesión en views.py:

Primero, necesitamos definir una vista en Django para manejar el proceso de inicio de sesión. Agrega el siguiente código al archivo views.py:

...
...
from django.contrib.auth import authenticate, login

def do_signin(request):
    if request.method == 'POST':
        data = json.loads(request.body)  
        username = data['username']
        password = data['password']

        user = authenticate(username=username, password=password)
        if user is not None:
            login(request, user)
            return JsonResponse({'success': True, 'message': 'OK'}, status=200)
        else:                        
            return JsonResponse({'success': True, 'message': 'Usuario o contraseña inválidos.'}, status=401)
    else:        
        return render(request, 'signin.html')

Enter fullscreen mode Exit fullscreen mode
  • Configurar la URL para la vista de Inicio de Sesión en urls.py:

Después de definir la vista de inicio de sesión, necesitamos configurar una URL para ella en el archivo urls.py de la aplicación. Puedes agregar el siguiente código:

from django.urls import path
from . import views

urlpatterns = [
  # Otras rutas
  path('signin/', views.do_signin, name='signin')
]
Enter fullscreen mode Exit fullscreen mode

¡Eso es todo! Ahora deberías tener un flujo de inicio de sesión funcional. Cuando accedas a la URL http://localhost:8000/signin en tu navegador, verás el formulario de inicio de sesión renderizado. Una vez que inicies sesión correctamente, serás redirigido a la página de inicio.

Paso 5: Cierre de sesión

Ahora veremos como implementar la funcionalidad de cierre de sesión (logout) en Django:

  • Definir la Vista de Cierre de Sesión en views.py:

Primero, necesitamos definir una vista en Django para manejar el proceso de cierre de sesión. Agrega el siguiente código a tu archivo views.py:

...
...
from django.contrib.auth import logout


def do_logout(request):
    logout(request)
    return redirect('signin')

Enter fullscreen mode Exit fullscreen mode
  • Configurar una URL para la vista de Cierre de Sesión en urls.py:

Después de definir la vista de cierre de sesión, necesitamos configurar una URL para ella en el archivo urls.py de la aplicación, de la siguiente forma:

from django.urls import path
from . import views

urlpatterns = [
  # Otras rutas
  path('logout/', views.do_logout, name='logout')
]
Enter fullscreen mode Exit fullscreen mode

¡Y eso es todo! Ahora deberías tener la funcionalidad de cierre de sesión implementada en tu aplicación Django. Ejecuta en tu navegador la URL http://localhost:8000/logout y debes ser redirigido a la página de inicio de sesión.

Da clic aquí para ir a la cuarta parte de este tutorial.

. . . . . . . . . . . .
Terabox Video Player