- Introducción a Ajax
1.1 Concepto
AJAX (Asynchronous JavaScript and XML) es una técnica de desarrollo web que permite la actualización asíncrona de páginas. Esto significa que se puede enviar y recibir datos del servidor sin necesidad de recargar toda la página, mejorando la experiencia del usuario.
Las tecnologías centrales de Ajax incluyen JavaScript, el objeto XMLHttpRequest para la comunicación con el servidor, y el DOM (Modelo de Objetos del Documento) para actualizar dinámicamente el contenido. Aunque originalmente utilizaba XML para el intercambio de datos, actualmente el formato JSON es el más común.
Una de sus principales ventajas es que, al no recargar la página completa, las interacciones se sienten más rápidas y fluidas. Ajax no requiere plugins adicionales, solo que el navegador tenga JavaScript habilitado.
1.2 Propósito principal
El objetivo fundamental de Ajax es permitir el envío de datos de forma asíncrona y la actualización parcial del contenido. Esto es útil, por ejemplo, para verificar la disponibilidad de un nombre de usuario en un formulario de registro sin interrumpir la experiencia del usuario.
1.3 Consideraciones adicionales
Técnicamente, Ajax es código JavaScript y, por lo general, requiere incluir una biblioteca como jQuery para su uso simplificado. A diferencia de los formularios tradicionales, que al enviarse recargan la página y limpian los campos, las solicitudes Ajax mantienen el estado de la página.
1.4 Métodos de solicitud al servidor
Existen varias formas de enviar solicitudes a un servidor desde el navegador: escribiendo una URL directamente, utilizando enlaces (<a>), mediante formularios HTML o a través de Ajax. Cada método puede usar distintos verbos HTTP como GET o POST.
- Ejemplos prácticos con Ajax
2.1 Calculadora asíncrona
Objetivo: Crear una página con dos campos de entrada para números y un botón. Al hacer clic, se enviará una solicitud Ajax al backend para calcular la suma, y el resultado se mostrará en un tercer campo sin recargar la página.
Implementación:
Configuración inicial del proyecto (models, urls, vistas) omitida por brevedad. A continuación, el código del template y la vista.
<!-- template: calculadora.html -->
<html>
<head>
<title>Calculadora Ajax</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body>
<input type="number" id="numA"> + <input type="number" id="numB"> =
<input type="number" id="resultado" readonly>
<button id="botonCalcular">Calcular</button>
<script>
$(document).ready(function(){
$('#botonCalcular').click(function(){
let valor1 = $('#numA').val();
let valor2 = $('#numB').val();
$.ajax({
url: '/calcular-suma/',
method: 'POST',
data: {
numero1: valor1,
numero2: valor2
},
success: function(respuesta) {
$('#resultado').val(respuesta.resultado);
}
});
});
});
</script>
</body>
</html>
# views.py
from django.http import JsonResponse
def calcular_suma(request):
if request.method == 'POST':
n1 = request.POST.get('numero1')
n2 = request.POST.get('numero2')
try:
total = float(n1) + float(n2)
return JsonResponse({'resultado': total})
except ValueError:
return JsonResponse({'error': 'Entrada no válida'}, status=400)
return render(request, 'calculadora.html')
2.2 Validación de nombre de usuario en tiempo real
Este ejemplo demuestra cómo usar Ajax para verificar la disponibilidad de un nombre de usuario mientras el usuario escribe.
# En el template
<label>Nombre de usuario: <input type="text" id="campoUsuario"></label>
<span id="mensajeDisponibilidad"></span>
<script>
$('#campoUsuario').on('input', function() {
let nombreIngresado = $(this).val();
if (nombreIngresado.length > 2) {
$.ajax({
url: '/verificar-usuario/',
data: {'nombre': nombreIngresado},
success: function(datos) {
if (datos.existe) {
$('#mensajeDisponibilidad').text('Nombre no disponible').css('color', 'red');
} else {
$('#mensajeDisponibilidad').text('Nombre disponible').css('color', 'green');
}
}
});
}
});
</script>
# views.py
from django.http import JsonResponse
def verificar_usuario(request):
nombre = request.GET.get('nombre')
existe = Usuario.objects.filter(username=nombre).exists()
return JsonResponse({'existe': existe})
- Formatos de codificación en la transmisión de datos
Los formatos comunes para enviar datos entre cliente y servidor son application/x-www-form-urlencoded, multipart/form-data y application/json. El encabezado HTTP Content-Type indica el formato utilizado.
3.1 Envío de datos de formulario (urlencoded)
Es el formato predeterminado para los formularios HTML. Los datos se codifican como pares clave-valor separados por '&'.
3.2 Envío de archivos (multipart/form-data)
Necesario para enviar archivos. El formulario debe especificar enctype="multipart/form-data". En Django, los archivos se acceden a través de request.FILES.
- Envío de datos JSON y archivos mediante Ajax
4.1 Conocimientos previos sobre JSON
JSON (JavaScript Object Notation) es un formato de intercambio de datos ligero. Un JSON válido usa comillas dobles para las claves y los valores de tipo cadena.
// Ejemplo de objeto JSON válido
{
"nombre": "Ana",
"edad": 28,
"habilidades": ["Python", "Django"]
}
En JavaScript, se puede convertir un string JSON a objeto con JSON.parse() y un objeto a string con JSON.stringify().
4.2 Envío de datos JSON con Ajax
Para enviar datos en formato JSON, es crucial establecer el contentType a application/json. En el back end de Django, estos datos se encuentran en request.body y deben ser decodificados manualmente.
// En el frontend
$.ajax({
url: '/recibir-json/',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({ usuario: "pepito", clave: "12345" }),
success: function(resp) { /* manejar respuesta */ }
});
# En el backend (views.py)
import json
def recibir_json(request):
datos = json.loads(request.body.decode('utf-8'))
# Procesar el diccionario 'datos'
return JsonResponse({"status": "ok"})
4.3 Envío de archivos con Ajax
Para enviar archivos, se utiliza el objeto FormData de JavaScript. Es indispensable deshabilitar el procesamiento y codificación automáticos de jQuery.
let formData = new FormData();
formData.append('archivo', $('#inputArchivo')[0].files[0]);
formData.append('campoTexto', 'valor');
$.ajax({
url: '/subir-archivo/',
type: 'POST',
data: formData,
contentType: false,
processData: false,
success: function() { /* éxito */ }
});
En Django, los datos del formulario se encuentran en request.POST y los archivos en request.FILES.
- Serialización de datos en Django
5.1 Serialización manual con JsonResponse
Cuando se necesita enviar una lista de objetos, como usuarios de la base de datos, a menudo es necesario convertir los objetos QuerySet a una lista de diccionarios.
# Serialización manual
def listar_usuarios(request):
usuarios = Usuario.objects.all()
lista_datos = []
for u in usuarios:
lista_datos.append({
'id': u.pk,
'nombre': u.nombre_completo,
'email': u.email
})
return JsonResponse(lista_datos, safe=False)
5.2 Uso del módulo de serialización de Django
Django provee un módulo de serialización que convierte objetos de modelos a formatos como JSON de manera sencilla.
from django.core import serializers
def exportar_usuarios(request):
queryset = Usuario.objects.all()
datos_json = serializers.serialize('json', queryset)
return HttpResponse(datos_json, content_type='application/json')
- Inserción masiva de datos
Cuando se necesita insertar una gran cantidad de registros, realizar inserciones individuales (create()) es muy ineficiente. La solución es utilizar el método bulk_create del ORM de Django.
# Inserción masiva ineficiente (1000 consultas SQL)
for i in range(1000):
Libro.objects.create(titulo=f"Libro {i}")
# Inserción masiva eficiente (1 sola consulta SQL)
lista_libros = [Libro(titulo=f"Libro {i}") for i in range(1000)]
Libro.objects.bulk_create(lista_libros)
Esto reduce drásticamente el tiempo de ejecución al minimizar las interacciones con la base de datos.
- Sistema de paginación
7.1 Concepto y cálculo básico
La paginación divide un conjunto grande de datos en páginas más pequeñas y manejables. Los parámetros clave son: página actual, cantidad de elementos por página y el total de elementos.
El cálculo para determinar qué porción de datos mostrar en una página específica se basa en fórmulas simples:
inicio = (pagina_actual - 1) * elementos_por_pagina
fin = pagina_actual * elementos_por_pagina
7.2 Implementación básica de paginación
# views.py
def mostrar_libros(request):
todos_los_libros = Libro.objects.all()
pagina_actual = request.GET.get('pagina', 1)
try:
pagina_actual = int(pagina_actual)
except (ValueError, TypeError):
pagina_actual = 1
libros_por_pagina = 10
inicio = (pagina_actual - 1) * libros_por_pagina
fin = inicio + libros_por_pagina
libros_pagina = todos_los_libros[inicio:fin]
return render(request, 'libros.html', {'libros': libros_pagina})
7.3 Cálculo dinámico del total de páginas
Para generar los enlaces de paginación, se necesita calcular el número total de páginas. La función divmod de Python es útil para esto.
total_elementos = 105
elementos_por_pagina = 10
paginas_completas, sobrante = divmod(total_elementos, elementos_por_pagina)
total_paginas = paginas_completas + (1 if sobrante else 0)
7.4 Creación de un componente de paginación reutilizable
Para evitar repetir código de paginación en múltiples vistas, se puede encapsular la lógica en una clase.
# utils/paginador.py
class Paginador:
def __init__(self, datos_totales, elementos_por_pagina=10, paginas_visibles=5):
self.total_datos = len(datos_totales)
self.elementos_por_pagina = elementos_por_pagina
self.total_paginas = (self.total_datos + elementos_por_pagina - 1) // elementos_por_pagina
self.paginas_visibles = paginas_visibles
def obtener_elementos(self, pagina_actual):
inicio = (pagina_actual - 1) * self.elementos_por_pagina
fin = inicio + self.elementos_por_pagina
return self.total_datos[inicio:fin]
def generar_html_paginacion(self, pagina_actual, url_base):
# Lógica para generar los enlaces HTML de paginación
# Considerando un rango centrado en la página actual
inicio_rango = max(1, pagina_actual - self.paginas_visibles // 2)
fin_rango = min(self.total_paginas, inicio_rango + self.paginas_visibles - 1)
# ... (código para generar los botones)
return html_enlaces
Este componente se puede usar en diferentes vistas proporcionándole los datos y la página actual.