La única forma de aprender a modelar es modelando. Implementa el modelo v1 y luego responde los retos. No mires solutions.md hasta entregar.
Preparación:
cd ~/dev/ticketflow && source .venv/bin/activateEjercicio 1 — Implementa el modelo v1
Crea en events/models.py las clases de la lección: TimeStampedModel, User, Event, Seat, Reservation, ReservationItem y Payment (con sus TextChoices).
Registra la app en settings.py (INSTALLED_APPS), y declara en settings.py:
AUTH_USER_MODEL = "events.User"Hazlo antes de la primera migración. Cambiar
AUTH_USER_MODELa mitad de proyecto es una de las migraciones más dolorosas de Django; por eso lo decidimos ahora (Lección 11 adelantada: las decisiones de esquema se toman antes de que existan datos).
Aplica:
python manage.py makemigrations
python manage.py migrateEntrega: el listado de modelos que te imprime python manage.py makemigrations --dry-run -v2 (o simplemente confirma que migró sin errores y pega el resumen).
Ejercicio 2 — Demuestra la invariante I1 en la consola
Abre python manage.py shell y intenta romper la invariante: dos reservas activas con el mismo asiento.
from events.models import *
u1 = User.objects.create(email="ana@example.com")
u2 = User.objects.create(email="beto@example.com")
ev = Event.objects.create(title="Concierto", venue="Sala X", starts_at="2027-03-01T21:00:00Z", state=EventState.PUBLISHED, organizer=u1)
s1 = Seat.objects.create(event=ev, row="A", number=1)
s2 = Seat.objects.create(event=ev, row="A", number=2)
r1 = Reservation.objects.create(user=u1, event=ev, status=ReservationState.PENDING_PAYMENT, expires_at="2026-10-01T12:00:00Z")
ReservationItem.objects.create(reservation=r1, seat=s1, price_at_purchase="50.00")
r2 = Reservation.objects.create(user=u2, event=ev, status=ReservationState.PENDING_PAYMENT, expires_at="2026-10-01T12:00:00Z")
ReservationItem.objects.create(reservation=r2, seat=s1, price_at_purchase="50.00") # ← ¿debe explotar?- ¿Qué excepción te lanza la última línea y con qué mensaje?
- Borra temporalmente las dos
UniqueConstraintcondicionales del modelo, rehaz migración y repite el experimento. ¿Se crea la segunda reserva? ¿Qué significa eso para el negocio? - Restaura las constraints. Anota en 2 líneas qué acabas de demostrar (esto es la prueba que contarás en una entrevista).
Ejercicio 3 — Consulta de disponibilidad
Escribe (en shell, o en events/services.py si te atreves) una función available_seats(event_id) que devuelva los asientos del evento sin reserva activa ni confirmada.
- Escríbela sin usar SQL crudo (pista: subconsulta o
excludesobre relación inversa). - Imprime el número de queries que ejecuta con
django.db.connection.queriesodjango-debug-toolbar... o simplemente conlen(connection.queries)tras llamarla dos veces. - ¿Ejecuta 1 query o N? (Guarda tu respuesta: la retomaremos en la Lección 09 con
EXPLAIN.)
Ejercicio 4 — Máquina de estado en el modelo
Añade a Reservation los métodos con transiciones legales:
def confirm(self, when):
...
def cancel(self, when):
...Reglas: solo se confirma una reserva PENDING_PAYMENT con expires_at no vencido y un pago SUCCEEDED; solo se cancela si no está ya cancelada (cancelar algo cancelado debe ser idempotente: no falla, no cambia nada).
- Implementa ambos métodos lanzando
ValueError(o una excepción de dominio propia) en transiciones ilegales. - En shell: crea una reserva vencida (
expires_aten el pasado) e intentaconfirm(). ¿Qué ocurre? - Ejecuta
cancel()dos veces. ¿La segunda es un no-op silencioso o lanza? Justifica tu elección.
Ejercicio 5 — Diseña lo que la v1 dejó fuera (discusión)
Responde razonando trade-offs (2-3 frases por pregunta, no hace falta código):
- El organizador quiere "fila VIP A completa" como producto aparte con precio distinto. ¿Qué cambiarías en el modelo? ¿Merece la pena para v1?
- TicketFlow quiere permitir "hasta 4 asientos por usuario por evento". ¿Dónde vives esa regla: constraint de BD, validación de serializer, o método del modelo? ¿Por qué?
- Un competidor vende "entradas generales sin asiento asignado" (solo aforo). ¿Reutilizarías
Seato modelarías otro tipo de ticket? ¿Qué rompe cada opción?
Ejercicio 6 — Reflexión senior (minutos 5)
Imagina que en 6 meses TicketFlow tiene 500 eventos y 2M de asientos. Con el modelo v1:
- ¿Qué consulta te preocupa más? (pista: la que hace todo el mundo cada segundo)
- ¿Qué índice o qué tabla sientes que va a doler primero?
- ¿Qué harías hoy —barato de hacer ahora, carísimo después— para prepararte?
Entrega
Pega en el chat tus resultados (código, excepciones y respuestas). Te corrijo uno a uno, y con esto cerramos la 00b y la marcamos dominada. Después: Lección 01 — HTTP a fondo si no la hiciste, o Lección 02 — Redes si ya entregaste la 01.