Introducción
En este tutorial aprenderás a:
- Registrar un usuario: Se validarán los datos usando Django Forms y se procesará el registro vía AJAX con Axios.
- Iniciar sesión: Se realizará la autenticación del usuario y, mediante AJAX, se mostrará el resultado (éxito o error) sin recargar la página.
- Cerrar sesión: Se implementará una función de logout que, también mediante AJAX, cerrará la sesión del usuario.
- Actualizar la plantilla base: Se mostrará el nombre de usuario activo en el encabezado si el usuario ha iniciado sesión.
A lo largo del tutorial se separará la lógica de validación (usando Django Forms) de la presentación (las plantillas HTML), y se desarrollará todo el código JavaScript en archivos externos para mantener el HTML limpio y organizado.
1. Formularios con Django Forms
Aunque la plantilla del formulario se construya manualmente, es recomendable centralizar la validación en Django. Crea o edita el archivo store/forms.py:
# store/forms.py
from django import forms
from django.contrib.auth.models import User
class SignupForm(forms.ModelForm):
password = forms.CharField(
widget=forms.PasswordInput,
min_length=8, max_length=12,
label="Contraseña"
)
confirm_password = forms.CharField(
widget=forms.PasswordInput,
min_length=8, max_length=12,
label="Confirmar Contraseña"
)
class Meta:
model = User
fields = ['first_name', 'last_name', 'username', 'email', 'password']
labels = {
'first_name': 'Nombre'
}
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
if password and confirm_password and password != confirm_password:
self.add_error('confirm_password', "Las contraseñas no coinciden.")
Importante: Aunque en la plantilla se construyen manualmente los campos, definir los labels y las validaciones en el formulario permite reutilizar esta lógica en el backend y mantener el código organizado.
2. Vistas para Registro, Inicio y Cierre de Sesión
Se separarán dos tipos de vistas:
- Vistas para renderizar las plantillas: Cuando el usuario navegue a las páginas de registro o inicio de sesión.
- Vistas para procesar los datos vía AJAX: Que recibirán los datos del formulario, realizarán las validaciones y devolverán respuestas en formato JSON.
2.1. Vistas para Renderizar las Plantillas
En store/views.py se utilizarán las funciones existentes para mostrar los formularios:
from django.shortcuts import render
def signup(request):
return render(request, 'signup.html')
def signin(request):
return render(request, 'signin.html')
2.2. Vistas para Procesamiento AJAX
Agrega en store/views.py las siguientes funciones para procesar los datos enviados mediante AJAX:
import json
from django.contrib.auth.models import User
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.contrib.auth import authenticate, login, logout
from .forms import SignupForm
@require_POST
def ajax_signup(request):
form = SignupForm(request.POST)
if form.is_valid():
user = form.save(commit=False)
# Encriptamos la contraseña correctamente
user.set_password(form.cleaned_data['password'])
user.save()
return JsonResponse({'success': True, 'message': 'Usuario registrado exitosamente.'})
else:
return JsonResponse({'success': False, 'errors': form.errors})
@require_POST
def ajax_signin(request):
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
return JsonResponse({'success': True, 'message': 'Inicio de sesión exitoso.'})
else:
return JsonResponse({'success': False, 'errors': {'__all__': "Credenciales incorrectas."}})
def ajax_logout(request):
logout(request)
return JsonResponse({'success': True, 'message': 'Sesión cerrada exitosamente.'})
3. Configuración de URLs
En store/urls.py define las rutas para renderizar las plantillas y para las solicitudes AJAX. Por ejemplo:
# store/urls.py
from django.urls import path
from . import views
urlpatterns = [
# Rutas para renderizar las plantillas:
path('signup/', views.signup, name='signup'),
path('signin/', views.signin, name='signin'),
# Rutas para procesar solicitudes AJAX:
path('ajax/signup/', views.ajax_signup, name='ajax_signup'),
path('ajax/signin/', views.ajax_signin, name='ajax_signin'),
path('logout/', views.ajax_logout, name='logout'),
# Otras rutas de la aplicación...
]
Con esta configuración, cuando un usuario visite /signup/ o /signin/ se mostrará el formulario correspondiente; y al enviar el formulario, el código JavaScript realizará la petición a /ajax/signup/ o /ajax/signin/.
4. JavaScript Externo con Axios
Crearemos un archivo JavaScript externo para manejar las llamadas AJAX utilizando Axios. Esto permite mantener el código de interacción fuera de las plantillas HTML.
Crea el archivo static/js/auth.js con el siguiente contenido:
// static/js/auth.js
// Función para manejar el envío del formulario de registro vía AJAX
function submitSignupForm() {
const form = document.getElementById('signup-form');
form.addEventListener('submit', function(e) {
e.preventDefault();
// Limpiar errores anteriores
document.querySelectorAll('.error').forEach(el => el.innerText = '');
const formData = new FormData(form);
axios.post(signupUrl, formData, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken': csrfToken
}
})
.then(response => {
if(response.data.success) {
alert(response.data.message);
window.location.href = signinUrl;
} else {
// Mostrar errores en cada campo
const errors = response.data.errors;
for(let field in errors) {
const errorElem = document.getElementById('error-' + field);
if (errorElem) {
errorElem.innerText = errors[field];
}
}
}
})
.catch(error => console.error('Error:', error));
});
}
// Función para manejar el envío del formulario de inicio de sesión vía AJAX
function submitSigninForm() {
const form = document.getElementById('signin-form');
form.addEventListener('submit', function(e) {
e.preventDefault();
document.querySelectorAll('.error').forEach(el => el.innerText = '');
const formData = new FormData(form);
axios.post(signinUrl, formData, {
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRFToken': csrfToken
}
})
.then(response => {
if(response.data.success) {
alert(response.data.message);
window.location.href = homeUrl;
} else {
if(response.data.errors.__all__){
alert(response.data.errors.__all__);
} else {
const errors = response.data.errors;
for(let field in errors) {
const errorElem = document.getElementById('error-' + field);
if (errorElem) {
errorElem.innerText = errors[field];
}
}
}
}
})
.catch(error => console.error('Error:', error));
});
}
// Función para cerrar sesión vía AJAX
function logoutUser() {
axios.get(logoutUrl, {
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(response => {
if(response.data.success){
window.location.href = homeUrl;
}
})
.catch(error => console.error('Error:', error));
}
// Inicialización: se ejecuta cuando la página carga
document.addEventListener('DOMContentLoaded', function() {
if(document.getElementById('signup-form')) {
submitSignupForm();
}
if(document.getElementById('signin-form')) {
submitSigninForm();
}
});
Variables Globales:
Este archivo utiliza las variablessignupUrl
,signinUrl
,logoutUrl
,homeUrl
ycsrfToken
. Estas deben definirse en la plantilla base para que Axios las utilice correctamente.
5. Actualización de las Plantillas HTML
5.1. Plantilla Base (base.html)
Actualiza store/templates/base.html para incluir Axios, el archivo JavaScript externo y para mostrar el nombre del usuario si hay sesión activa:
{% load static %}
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EELA Ecommerce - {% block title %}{% endblock %}</title>
<meta name="keywords" content="Tienda en línea, Ecuador">
<meta name="author" content="Gabriel Villacis">
<link rel="stylesheet" href="{% static 'css/styles.css' %}">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
<link rel="shortcut icon" href="{% static 'img/favicon.png' %}" type="image/x-icon">
</head>
<body>
<header class="main-header">
<div class="container">
<div id="brand">
<h1>EELA Ecommerce</h1>
</div>
<nav>
<ul>
<li><a href="{% url 'home' %}">Inicio</a></li>
<li><a href="{% url 'catalog' %}">Productos</a></li>
<li><a href="{% url 'cart' %}">Carrito</a></li>
<li><a href="{% url 'contact' %}">Contacto</a></li>
{% if user.is_authenticated %}
<li>Bienvenido, {{ user.username }}</li>
<li><button onclick="logoutUser()" class="btn btn-secondary">Cerrar Sesión</button></li>
{% else %}
<li><a href="{% url 'signup' %}">Registrarse</a></li>
<li><a href="{% url 'signin' %}">Iniciar Sesión</a></li>
{% endif %}
</ul>
</nav>
</div>
</header>
<main>
<div class="container">
{% block main_content %}{% endblock %}
</div>
</main>
<footer>
<p>© 2025 EELA Ecommerce - Todos los derechos reservados</p>
</footer>
<!-- Incluir Axios desde un CDN -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- Definir variables globales para Axios -->
<script>
var ajaxSignupUrl = "{% url 'ajax_signup' %}";
var ajaxSigninUrl = "{% url 'ajax_signin' %}";
var signinUrl = "{% url 'signin' %}";
var homeUrl = "{% url 'home' %}";
var logoutUrl = "{% url 'logout' %}";
var csrfToken = "{{ csrf_token }}";
</script>
<!-- Incluir el JavaScript de autenticación -->
<script src="{% static 'js/auth.js' %}"></script>
</body>
</html>
5.2. Plantilla de Registro (signup.html)
Edita la plantilla store/templates/signup.html:
{% extends 'base.html' %}
{% block title %}Registrarse{% endblock %}
{% block main_content %}
<h2>Registrarse</h2>
<div class="form-container">
<form id="signup-form">
<div class="form-group">
<label for="name">Nombre:</label>
<input type="text" name="first_name" id="name" required maxlength="40">
<div class="error" id="error-first_name"></div>
</div>
<div class="form-group">
<label for="name">Apellido:</label>
<input type="text" name="last_name" id="last_name" required maxlength="40">
<div class="error" id="error-last_name"></div>
</div>
<div class="form-group">
<label for="username">Usuario:</label>
<input type="text" name="username" id="username" required minlength="10" maxlength="20">
<div class="error" id="error-username"></div>
</div>
<div class="form-group">
<label for="email">Correo electrónico:</label>
<input type="email" name="email" id="email" required>
<div class="error" id="error-email"></div>
</div>
<div class="form-group">
<label for="password">Contraseña:</label>
<input type="password" name="password" id="password" required minlength="8" maxlength="12">
<div class="error" id="error-password"></div>
</div>
<div class="form-group">
<label for="confirm_password">Confirmar Contraseña:</label>
<input type="password" name="confirm_password" id="confirm_password" required minlength="8" maxlength="12">
<div class="error" id="error-confirm_password"></div>
</div>
<input type="submit" value="Registrarse" class="btn">
<input type="reset" value="Cancelar" class="btn btn-secondary">
</form>
</div>
{% endblock %}
5.3. Plantilla de Inicio de Sesión (signin.html)
Crea o edita store/templates/signin.html:
{% extends 'base.html' %}
{% block title %}Iniciar Sesión{% endblock %}
{% block main_content %}
<h2>Iniciar Sesión</h2>
<div class="form-container">
<form id="signin-form">
<div class="form-group">
<label for="username">Usuario:</label>
<input type="text" name="username" id="username" required minlength="10" maxlength="20">
<div class="error" id="error-username"></div>
</div>
<div class="form-group">
<label for="password">Contraseña:</label>
<input type="password" name="password" id="password" required>
<div class="error" id="error-password"></div>
</div>
<input type="submit" value="Iniciar Sesión" class="btn">
<input type="reset" value="Cancelar" class="btn btn-secondary">
</form>
</div>
{% endblock %}
Conclusión
En este tutorial se implementaron de manera integral las funcionalidades de registro, inicio de sesión y cierre de sesión en Django utilizando el modelo de usuario predeterminado, validaciones centralizadas en Django Forms y llamadas AJAX mediante Axios. Además, se movió el código JavaScript a un archivo externo para mejorar la organización y se actualizó la plantilla base para mostrar el nombre del usuario cuando existe una sesión activa.
Estos pasos permiten ofrecer una experiencia de usuario moderna y fluida, con validación robusta y comunicación asíncrona entre el front end y el back end. ¡Continúa ampliando y personalizando tu proyecto según las necesidades de tu ecommerce!
Top comments (0)