Django Guía

Modelos y ORM: De 0 a Experto 🚀

Esta guía te llevará desde la definición básica de tablas hasta consultas complejas y optimizadas con el ORM de Django.

1. Anatomía de un Modelo Experto

Un modelo robusto utiliza los tipos de datos correctos para garantizar la integridad y el rendimiento.

# src/conceptos_basicos/models.py
from django.db import models

class Curso(models.Model):
    # Enum para opciones limitadas (Django 3.0+)
    NIVEL_CHOICES = [
        ('BASICO', 'Básico'),
        ('INTERMEDIO', 'Intermedio'),
        ('AVANZADO', 'Avanzado'),
    ]

    titulo = models.CharField(max_length=200, verbose_name="Título")

    # DecimalField es CRÍTICO para dinero. Float pierde precisión.
    precio = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

    # BooleanField con valor por defecto
    publicado = models.BooleanField(default=False)

    # Choices limita la entrada a valores predefinidos en la BD y Admin
    nivel = models.CharField(max_length=10, choices=NIVEL_CHOICES, default='BASICO')

2. Relaciones entre Tablas

El poder de las bases de datos relacionales reside en... las relaciones. Django ofrece tres tipos principales:

A. ForeignKey (Uno a Muchos)

Un Curso pertenece a una Categoria. Una Categoria tiene muchos Cursos.

class Categoria(models.Model):
    nombre = models.CharField(max_length=50)

class Curso(models.Model):
    # related_name='cursos' permite acceder desde categoria: mi_categoria.cursos.all()
    categoria = models.ForeignKey(Categoria, on_delete=models.SET_NULL, null=True, related_name="cursos")

B. ManyToManyField (Muchos a Muchos)

Un Estudiante puede tomar muchos Cursos. Un Curso tiene muchos Estudiantes.

class Estudiante(models.Model):
    # Django crea una tabla intermedia automáticamente
    cursos = models.ManyToManyField(Curso, related_name="estudiantes")

C. OneToOneField (Uno a Uno)

Extender información de un usuario o entidad.

class PerfilEstudiante(models.Model):
    estudiante = models.OneToOneField(Estudiante, on_delete=models.CASCADE)
    bio = models.TextField()

3. Consultas con el ORM (El Poder Real)

Aquí es donde dejamos de ser principiantes. Ejemplos reales validados en nuestros tests (tests/test_orm_avanzado.py).

Filtrado Básico y Lookups

Los "lookups" se añaden al nombre del campo con doble guion bajo (__).

# Obtener cursos que contengan "django" (ignora mayúsculas/minúsculas)
Curso.objects.filter(titulo__icontains="django")

# Cursos GRATUITOS (precio menor o igual a 0)
Curso.objects.filter(precio__lte=0)

# Cursos publicados en el año 2024
Curso.objects.filter(fecha_inicio__year=2024)

Consultas Avanzadas con Q Objects

Cuando necesitas lógica OR (|) o negaciones complejas (~), usas objetos Q.

from django.db.models import Q

# Quiero cursos que sean GRATIS O que sean nivel AVANZADO
Curso.objects.filter(Q(precio=0) | Q(nivel="AVANZADO"))

Relaciones Inversas (Spanning)

Puedes filtrar basándote en campos de modelos relacionados.

# Todos los cursos de la categoría "Desarrollo Web"
Curso.objects.filter(categoria__nombre="Desarrollo Web")

# Todos los estudiantes inscritos en cursos "Avanzados"
Estudiante.objects.filter(cursos__nivel="AVANZADO").distinct()

Agregación y Anotación

Operaciones matemáticas sobre grupos de datos.

from django.db.models import Avg, Count

# ¿Cuál es el precio promedio de mis cursos?
Curso.objects.aggregate(Avg('precio'))
# Retorna: {'precio__avg': 49.99}

# Añadir un campo 'num_estudiantes' a cada curso en la consulta
cursos = Curso.objects.annotate(num_estudiantes=Count('estudiantes'))
for c in cursos:
    print(f"{c.titulo}: {c.num_estudiantes} alumnos")

Expresiones F (Atomic Updates)

Actualizar un campo basándose en su valor actual, directamente en la base de datos (sin race conditions).

from django.db.models import F

# Incrementa el precio de cada curso en un 10%
Curso.objects.update(precio=F('precio') * 1.1)