Café y Código
RUTA DE APRENDIZAJE · PARTE 1 DE 3LECCIONES 01–06

7.1 Guía Práctica: CRUD y Persistencia 🎬

Del modelo de datos al CRUD completo con formularios HTML puros, validación en servidor con request.POST y alertas con messages.

6 lecciones en video con fundamentos técnicos, código comentado y actividades de aplicación.

¿Cómo trabajar esta guía?

Revisa cada video y utiliza el material complementario para comprender qué ocurre en el modelo, la vista, la plantilla y el navegador. Reproduce los ejemplos en un proyecto Django y verifica cada resultado antes de avanzar. Las explicaciones complementan los temas de los videos; no son transcripciones de su contenido.

Regla del curso: no utilizar ProductoForm ni ModelForm. Construir los formularios directamente en HTML y procesar los datos con request.POST, validándolos en la vista.
PARTE 1 · LECCIONES 01–06

CRUD y persistencia de datos

Aprende a estructurar los datos y desarrollar las operaciones de consulta, creación, actualización y eliminación.

LECCIÓN 01

Modelo y base de datos

Objetivo: Comprender cómo Django representa las tablas mediante clases Python.

¿Qué aprenderás y por qué es importante?

El modelo constituye la estructura de información del sistema. Cada clase que hereda de models.Model representa una tabla; sus atributos representan columnas y cada objeto guardado corresponde a un registro. Django utiliza un ORM (mapeador objeto-relacional), que permite trabajar con Python en vez de escribir manualmente cada sentencia SQL.

Explicación técnica paso a paso

Antes de crear vistas o formularios, se deben definir los datos que necesita el negocio. En este ejemplo, cada producto tiene nombre, precio, stock y estado. Los tipos de campo determinan las validaciones básicas y el tipo de dato que se almacenará.

Ejemplo práctico · Python / Django
tienda/models.py
PYTHON
1# tienda/models.py
2from django.db import models
3
4class Producto(models.Model):
5 nombre = models.CharField(max_length=100)
6 precio = models.DecimalField(max_digits=10, decimal_places=2)
7 stock = models.PositiveIntegerField(default=0)
8 activo = models.BooleanField(default=True)
9
10 def __str__(self):
11 return self.nombre
Ejemplo complementario · Comandos / HTML
Terminal
BASH
1python manage.py makemigrations
2python manage.py migrate
Atención y buenas prácticas: makemigrations prepara los cambios del esquema y migrate los aplica a la base de datos. Registrar la aplicación en INSTALLED_APPS antes de ejecutar las migraciones.
Actividad de aplicación: Definir un modelo Categoria y relacionarlo con Producto mediante ForeignKey. Explicar por qué se usa on_delete=models.PROTECT.
LECCIÓN 02

Administración con Django Admin

Objetivo: Registrar modelos en el panel administrativo y comprender su utilidad.

¿Qué aprenderás y por qué es importante?

Django Admin es una interfaz de gestión incluida con el framework. Permite consultar, crear, modificar y eliminar registros de modelos registrados, siempre que el usuario tenga permisos. Es una herramienta especialmente útil para pruebas, carga inicial y tareas administrativas internas. No reemplaza automáticamente la interfaz que utilizarán los usuarios finales.

Explicación técnica paso a paso

Para habilitar el modelo se registra en admin.py. Un ModelAdmin permite definir columnas, filtros y búsquedas. El administrador requiere que estén instaladas las aplicaciones de autenticación, sesiones y administración incluidas en la configuración estándar.

Ejemplo práctico · Python / Django
tienda/admin.py
PYTHON
1# tienda/admin.py
2from django.contrib import admin
3from .models import Producto
4
5@admin.register(Producto)
6class ProductoAdmin(admin.ModelAdmin):
7 list_display = ('id', 'nombre', 'precio', 'stock', 'activo')
8 search_fields = ('nombre',)
9 list_filter = ('activo',)
Ejemplo complementario · Comandos / HTML
Terminal
BASH
1python manage.py createsuperuser
2python manage.py runserver
3# Abrir /admin/
Atención y buenas prácticas: El superusuario tiene acceso amplio. Para un entorno real se deben crear cuentas con permisos mínimos y evitar compartir credenciales.
Actividad de aplicación: Registrar el modelo, ingresar tres productos y comprobar cómo funcionan la búsqueda y el filtro.
LECCIÓN 03

Listar registros: SELECT, vista y tabla HTML

Objetivo: Recuperar registros desde el ORM y enviarlos a una plantilla.

¿Qué aprenderás y por qué es importante?

La vista recibe una solicitud HTTP, consulta el modelo y construye el contexto que se entrega a una plantilla HTML. La plantilla presenta los resultados al usuario. Esta separación ayuda a mantener el acceso a datos en Python y la presentación en HTML.

Explicación técnica paso a paso

Producto.objects.all() genera un QuerySet. Es posible ordenarlo, filtrarlo o limitarlo antes de mostrarlo. En el HTML, el lenguaje de plantillas de Django permite recorrer cada registro con for y acceder a sus atributos. No se deben escribir consultas SQL dentro de la plantilla.

Ejemplo práctico · Python / Django
tienda/views.py
PYTHON
1# tienda/views.py
2from django.shortcuts import render
3from .models import Producto
4
5def listado_productos(request):
6 productos = Producto.objects.all().order_by('nombre')
7 return render(request, 'tienda/listado.html', {
8 'productos': productos
9 })
Ejemplo complementario · Comandos / HTML
templates/tienda/listado.html
HTML
1<!-- templates/tienda/listado.html -->
2<table>
3 <thead><tr><th>Nombre</th><th>Precio</th><th>Stock</th></tr></thead>
4 <tbody>
5 {% for producto in productos %}
6 <tr>
7 <td>{{ producto.nombre }}</td>
8 <td>{{ producto.precio }}</td>
9 <td>{{ producto.stock }}</td>
10 </tr>
11 {% empty %}
12 <tr><td colspan="3">No hay productos registrados.</td></tr>
13 {% endfor %}
14 </tbody>
15</table>
Atención y buenas prácticas: Definir una ruta en urls.py y verificar que el nombre de la plantilla coincida. El bloque empty evita mostrar una tabla aparentemente rota cuando no existen registros.
Actividad de aplicación: Agregar una columna Estado y mostrar “Disponible” cuando stock sea mayor que cero.
LECCIÓN 04

Crear registros con formularios HTML y POST

Objetivo: Procesar el envío de un formulario desde la misma vista.

¿Qué aprenderás y por qué es importante?

El método GET normalmente muestra la página y el formulario; POST envía datos al servidor para solicitar la creación del registro. La vista puede atender ambos métodos: en GET devuelve el formulario vacío y en POST lee, valida y guarda los valores. La operación de escritura nunca debe confiar ciegamente en los datos recibidos.

Explicación técnica paso a paso

Se utiliza request.POST.get() para obtener los campos del formulario. El nombre de cada input debe coincidir exactamente con la clave consultada. El token CSRF protege contra solicitudes falsificadas. Se recomienda convertir números dentro de un bloque try, comprobar rangos y redirigir después de guardar para evitar envíos duplicados al actualizar la página.

Ejemplo práctico · Python / Django
tienda/views.py (fragmento)
PYTHON
1# tienda/views.py (fragmento)
2from decimal import Decimal, InvalidOperation
3from django.shortcuts import render, redirect
4from .models import Producto
5
6def crear_producto(request):
7 error = None
8 if request.method == 'POST':
9 nombre = request.POST.get('nombre', '').strip()
10 try:
11 precio = Decimal(request.POST.get('precio', ''))
12 stock = int(request.POST.get('stock', ''))
13 if not nombre or precio < 0 or stock < 0:
14 raise ValueError('Datos fuera de rango')
15 Producto.objects.create(nombre=nombre, precio=precio, stock=stock)
16 return redirect('listado_productos')
17 except (InvalidOperation, ValueError, TypeError):
18 error = 'Revisa el nombre, el precio y el stock.'
19 return render(request, 'tienda/crear.html', {'error': error})
Ejemplo complementario · Comandos / HTML
templates/tienda/crear.html
HTML
1<form method="post">
2 {% csrf_token %}
3 <label>Nombre <input name="nombre" required maxlength="100"></label>
4 <label>Precio <input name="precio" type="number" step="0.01" min="0" required></label>
5 <label>Stock <input name="stock" type="number" min="0" required></label>
6 <button type="submit">Guardar producto</button>
7</form>
8{% if error %}<p role="alert">{{ error }}</p>{% endif %}
Atención y buenas prácticas: No utilizar ProductoForm ni ModelForm en este curso. La validación HTML mejora la experiencia, pero siempre debe repetirse en el servidor.
Actividad de aplicación: Crear un producto y comprobar que aparece en el listado y en Django Admin. Probar el envío de un precio inválido.
LECCIÓN 05

Editar y eliminar registros

Objetivo: Actualizar registros existentes y realizar eliminaciones controladas.

¿Qué aprenderás y por qué es importante?

Editar consiste en recuperar un objeto existente, mostrar sus valores actuales y guardar los cambios. Eliminar consiste en localizar un registro y borrarlo de forma explícita. Ambas acciones deben trabajar con un identificador único y controlar el caso en que el objeto solicitado no existe.

Explicación técnica paso a paso

get_object_or_404 devuelve una respuesta 404 cuando el registro no existe. En la edición, GET muestra el formulario precargado y POST actualiza el objeto. La eliminación debe confirmarse y ejecutarse mediante POST, no mediante un simple enlace GET, porque GET no debe producir cambios destructivos.

Ejemplo práctico · Python / Django
tienda/views.py (fragmentos)
PYTHON
1# tienda/views.py (fragmentos)
2from django.shortcuts import get_object_or_404, redirect, render
3from decimal import Decimal, InvalidOperation
4
5def editar_producto(request, pk):
6 producto = get_object_or_404(Producto, pk=pk)
7 error = None
8 if request.method == 'POST':
9 try:
10 nombre = request.POST.get('nombre', '').strip()
11 precio = Decimal(request.POST.get('precio', ''))
12 stock = int(request.POST.get('stock', ''))
13 if not nombre or precio < 0 or stock < 0:
14 raise ValueError()
15 producto.nombre = nombre
16 producto.precio = precio
17 producto.stock = stock
18 producto.save()
19 return redirect('listado_productos')
20 except (InvalidOperation, ValueError, TypeError):
21 error = 'Los datos ingresados no son válidos.'
22 return render(request, 'tienda/editar.html', {'producto': producto, 'error': error})
23
24def eliminar_producto(request, pk):
25 producto = get_object_or_404(Producto, pk=pk)
26 if request.method == 'POST':
27 producto.delete()
28 return redirect('listado_productos')
29 return render(request, 'tienda/confirmar_eliminar.html', {'producto': producto})
Ejemplo complementario · Comandos / HTML
templates/tienda/confirmar_eliminar.html
HTML
1<!-- templates/tienda/confirmar_eliminar.html -->
2<h2>¿Eliminar {{ producto.nombre }}?</h2>
3<form method="post">
4 {% csrf_token %}
5 <button type="submit">Confirmar eliminación</button>
6</form>
7<a href="{% url 'listado_productos' %}">Cancelar</a>
Atención y buenas prácticas: En editar.html se usan los mismos campos HTML del formulario de creación, con value="{{ producto.nombre }}" y valores equivalentes para precio y stock. La URL debe enviar pk a la vista.
Actividad de aplicación: Editar el precio de un producto y confirmar el cambio; después eliminar otro producto desde la pantalla de confirmación.
LECCIÓN 06

Mensajes de éxito, advertencia y error

Objetivo: Informar al usuario el resultado de sus operaciones.

¿Qué aprenderás y por qué es importante?

El framework de mensajes de Django permite mostrar avisos temporales después de una operación. Por ejemplo, al guardar un producto se muestra “Producto creado correctamente”; al eliminar, “Producto eliminado”. Los mensajes mejoran la claridad del flujo y permiten distinguir acciones exitosas de errores o advertencias.

Explicación técnica paso a paso

Los mensajes se agregan desde la vista y se leen desde la plantilla. Con la configuración habitual de Django, pueden mantenerse durante una redirección. Es importante distinguir un mensaje informativo de una validación: mostrar un mensaje de éxito no demuestra por sí solo que los datos sean correctos.

Ejemplo práctico · Python / Django
tienda/views.py (fragmento)
PYTHON
1# tienda/views.py (fragmento)
2from django.contrib import messages
3
4# Después de crear correctamente:
5messages.success(request, 'Producto registrado correctamente.')
6return redirect('listado_productos')
7
8# Después de eliminar correctamente:
9messages.warning(request, 'Producto eliminado.')
10return redirect('listado_productos')
Ejemplo complementario · Comandos / HTML
templates/base.html
HTML
1<!-- En una plantilla base -->
2{% if messages %}
3 {% for message in messages %}
4 <div class="alerta {{ message.tags }}" role="status">
5 {{ message }}
6 </div>
7 {% endfor %}
8{% endif %}
Atención y buenas prácticas: Verificar que django.contrib.messages, su middleware y su context processor estén habilitados. No exponer contraseñas ni datos sensibles en mensajes.
Actividad de aplicación: Agregar mensajes al crear, editar y eliminar, y comprobar que aparecen después de cada redirección.

Ponte a prueba — Parte 1 (CRUD)

Comprueba tu dominio de las lecciones 01 a 06 sobre modelos, consultas ORM, formularios HTML con request.POST, get_object_or_404 y el sistema de mensajes.

Ko-fi
Donaciones
Apoyá cafeycodigo con un café en Ko-fi. Colaboradores: insignia, muro y zona exclusiva.