Los modelos son el corazón de Django y definen la estructura de tus datos. Son clases de Python que representan tablas en tu base de datos y proporcionan métodos para interactuar con ellas de forma segura y eficiente.
Un modelo en Django es una clase de Python que hereda de django.db.models.Model
. Cada atributo de la clase representa un campo en la tabla de la base de datos.
Django proporciona un sistema de mapeo objeto-relacional (ORM) que te permite interactuar con tu base de datos usando código Python en lugar de SQL directo.
El ORM de Django te protege contra ataques comunes como inyección SQL, ya que escapa automáticamente los parámetros de consulta.
Puedes cambiar de sistema de base de datos (MySQL, PostgreSQL, SQLite, etc.) sin cambiar tu código Python.
Los modelos incluyen un sistema de validación que verifica los datos antes de guardarlos en la base de datos.
El ORM proporciona una API Python clara y consistente para realizar consultas complejas sin escribir SQL.
Para crear un modelo en Django, defines una clase que hereda de django.db.models.Model
en el archivo models.py
de tu aplicación.
# En models.py
from django.db import models
class Producto(models.Model):
nombre = models.CharField(max_length=100)
descripcion = models.TextField(blank=True)
precio = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField(default=0)
fecha_creacion = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.nombre
Django sigue ciertas convenciones para traducir tus modelos a tablas en la base de datos:
Elemento | Convención | Ejemplo |
---|---|---|
Nombre de tabla | [app_name]_[model_name] |
tienda_producto |
Clave primaria | Campo id automático |
id |
Relación ForeignKey | [campo]_id |
categoria_id |
Meta
dentro de tu modelo:
class Producto(models.Model):
# campos del modelo...
class Meta:
db_table = 'productos'
verbose_name = 'Producto'
verbose_name_plural = 'Productos'
Django proporciona muchos tipos de campos para representar diferentes tipos de datos en tus modelos.
Tipo de Campo | Descripción | Ejemplo |
---|---|---|
CharField |
Campo de texto de longitud limitada | nombre = models.CharField(max_length=100) |
TextField |
Campo de texto largo sin límite | descripcion = models.TextField() |
IntegerField |
Número entero | edad = models.IntegerField() |
FloatField |
Número decimal de precisión variable | altura = models.FloatField() |
DecimalField |
Número decimal de precisión fija | precio = models.DecimalField(max_digits=10, decimal_places=2) |
BooleanField |
Valor booleano (verdadero/falso) | activo = models.BooleanField(default=True) |
DateField |
Fecha (año, mes, día) | fecha_nacimiento = models.DateField() |
DateTimeField |
Fecha y hora | creado = models.DateTimeField(auto_now_add=True) |
EmailField |
Campo para direcciones de correo | email = models.EmailField() |
FileField |
Campo para archivos | archivo = models.FileField(upload_to='archivos/') |
ImageField |
Campo para imágenes | imagen = models.ImageField(upload_to='imagenes/') |
URLField |
Campo para URLs | sitio_web = models.URLField() |
Opción | Descripción | Ejemplo |
---|---|---|
null |
Si se permite NULL en la base de datos | fecha = models.DateField(null=True) |
blank |
Si se permite valor vacío en formularios | descripcion = models.TextField(blank=True) |
default |
Valor predeterminado | activo = models.BooleanField(default=True) |
choices |
Lista de opciones para el campo | status = models.CharField(choices=[('A', 'Activo'), ('I', 'Inactivo')], max_length=1) |
primary_key |
Define el campo como clave primaria | codigo = models.CharField(max_length=10, primary_key=True) |
unique |
El valor debe ser único | email = models.EmailField(unique=True) |
help_text |
Texto de ayuda para formularios | nombre = models.CharField(help_text="Ingrese el nombre del producto") |
verbose_name |
Nombre amigable para el campo | fecha_creacion = models.DateField(verbose_name="Fecha de creación") |
null=True
y blank=True
es que null
afecta a la base de datos, mientras que blank
afecta a la validación en formularios. Para campos de texto, se recomienda usar blank=True, default=""
en lugar de null=True
.
Django permite establecer relaciones entre modelos para representar estructuras de datos complejas.
Una categoría puede tener muchos productos, pero un producto pertenece a una sola categoría.
class Categoria(models.Model):
nombre = models.CharField(max_length=50)
def __str__(self):
return self.nombre
class Producto(models.Model):
# otros campos...
categoria = models.ForeignKey(
Categoria,
on_delete=models.CASCADE,
related_name='productos'
)
Un producto puede tener varias etiquetas, y una etiqueta puede aplicarse a varios productos.
class Etiqueta(models.Model):
nombre = models.CharField(max_length=30)
def __str__(self):
return self.nombre
class Producto(models.Model):
# otros campos...
etiquetas = models.ManyToManyField(
Etiqueta,
related_name='productos'
)
Un producto tiene exactamente un detalle técnico y viceversa.
class DetalleTecnico(models.Model):
especificaciones = models.TextField()
garantia = models.IntegerField()
def __str__(self):
return f"Detalle del producto {self.producto}"
class Producto(models.Model):
# otros campos...
detalle = models.OneToOneField(
DetalleTecnico,
on_delete=models.CASCADE,
related_name='producto'
)
Al definir una relación ForeignKey o OneToOneField, es obligatorio especificar qué ocurre cuando se elimina el objeto relacionado:
Opción | Descripción |
---|---|
models.CASCADE |
Elimina en cascada los objetos relacionados |
models.PROTECT |
Impide la eliminación si existen objetos relacionados |
models.SET_NULL |
Establece el campo en NULL (requiere null=True) |
models.SET_DEFAULT |
Establece el campo en su valor predeterminado |
models.DO_NOTHING |
No hace nada (puede causar inconsistencias) |
related_name
define el nombre con el que se accederá a los objetos relacionados desde el objeto relacionado. Si no se especifica, Django usa [model_name]_set
.
Las migraciones son la forma en que Django propaga los cambios en los modelos a la base de datos. Son como un sistema de control de versiones para tu esquema de base de datos.
models.py
python manage.py makemigrations
python manage.py migrate
Comando | Descripción |
---|---|
python manage.py showmigrations |
Muestra el estado de las migraciones (aplicadas o pendientes) |
python manage.py sqlmigrate app_name 0001 |
Muestra el SQL que generará la migración |
python manage.py migrate app_name 0002 |
Migra a una versión específica |
python manage.py migrate app_name zero |
Revierte todas las migraciones de una aplicación |
En estos casos, puede ser necesario crear migraciones manuales o utilizar estrategias específicas.
El ORM (Object-Relational Mapping) de Django te permite realizar consultas a la base de datos utilizando código Python en lugar de SQL directo.
# Método 1: Crear y guardar
producto = Producto()
producto.nombre = "Laptop Dell XPS 13"
producto.precio = 1299.99
producto.stock = 10
producto.save()
# Método 2: Crear con argumentos nombrados
producto = Producto(
nombre="MacBook Pro",
precio=1999.99,
stock=5
)
producto.save()
# Método 3: Crear directamente con create()
Producto.objects.create(
nombre="Surface Pro",
precio=1499.99,
stock=8
)
# Obtener todos los productos
productos = Producto.objects.all()
# Obtener un producto específico por ID
try:
producto = Producto.objects.get(id=1)
except Producto.DoesNotExist:
print("El producto no existe")
# Filtrar productos
productos_caros = Producto.objects.filter(precio__gt=1000)
laptops = Producto.objects.filter(nombre__contains="Laptop")
# Ordenar productos
productos_ordenados = Producto.objects.order_by('-precio') # Descendente
productos_baratos_primero = Producto.objects.order_by('precio') # Ascendente
# Limitar resultados
cinco_productos = Producto.objects.all()[:5]
# Contar
total_productos = Producto.objects.count()
# Consultas complejas con Q
from django.db.models import Q
productos_query = Producto.objects.filter(
Q(nombre__contains="Laptop") | Q(nombre__contains="Surface")
)
# Método 1: Actualizar un objeto individual
producto = Producto.objects.get(id=1)
producto.precio = 1399.99
producto.save()
# Método 2: Actualizar varios objetos a la vez
Producto.objects.filter(stock=0).update(disponible=False)
# Método 3: Actualización F() para operaciones atómicas
from django.db.models import F
Producto.objects.update(stock=F('stock') - 1) # Reduce el stock de todos los productos
# Método 1: Eliminar un objeto individual
producto = Producto.objects.get(id=1)
producto.delete()
# Método 2: Eliminar varios objetos a la vez
Producto.objects.filter(stock=0).delete()
# Método 3: Eliminar todos los objetos
Producto.objects.all().delete()
# Agregaciones
from django.db.models import Avg, Sum, Min, Max, Count
promedio_precio = Producto.objects.aggregate(Avg('precio'))
total_stock = Producto.objects.aggregate(Sum('stock'))
# Anotaciones
from django.db.models import F, ExpressionWrapper, DecimalField
productos_con_valor = Producto.objects.annotate(
valor_inventario=ExpressionWrapper(
F('precio') * F('stock'),
output_field=DecimalField()
)
)
# Relaciones
# Productos de una categoría específica
productos_electronica = Categoria.objects.get(nombre='Electrónica').productos.all()
# Categorías con más de 5 productos
categorias_populares = Categoria.objects.annotate(
num_productos=Count('productos')
).filter(num_productos__gt=5)
# Seleccionar campos específicos
nombres_productos = Producto.objects.values_list('nombre', flat=True)
info_productos = Producto.objects.values('nombre', 'precio')
print(Producto.objects.filter(precio__gt=1000).query)
Asumiendo que ya tienes un proyecto Django, crea una aplicación para el blog:
python manage.py startapp blog
Edita el archivo blog/models.py
:
from django.db import models
from django.contrib.auth.models import User
class Categoria(models.Model):
nombre = models.CharField(max_length=50, unique=True)
descripcion = models.TextField(blank=True)
def __str__(self):
return self.nombre
class Meta:
verbose_name_plural = "Categorías"
class Articulo(models.Model):
titulo = models.CharField(max_length=200)
contenido = models.TextField()
fecha_publicacion = models.DateTimeField(auto_now_add=True)
fecha_actualizacion = models.DateTimeField(auto_now=True)
autor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='articulos')
categorias = models.ManyToManyField(Categoria, related_name='articulos')
publicado = models.BooleanField(default=False)
def __str__(self):
return self.titulo
class Meta:
ordering = ['-fecha_publicacion']
class Comentario(models.Model):
articulo = models.ForeignKey(Articulo, on_delete=models.CASCADE, related_name='comentarios')
autor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comentarios')
contenido = models.TextField()
fecha = models.DateTimeField(auto_now_add=True)
aprobado = models.BooleanField(default=False)
def __str__(self):
return f"Comentario de {self.autor.username} en {self.articulo.titulo}"
class Meta:
ordering = ['fecha']
Agrega 'blog' a la lista INSTALLED_APPS en settings.py:
INSTALLED_APPS = [
# Otras aplicaciones...
'blog',
]
python manage.py makemigrations
python manage.py migrate
Abre el shell de Django y realiza algunas operaciones:
python manage.py shell
# Importar los modelos
from blog.models import Categoria, Articulo, Comentario
from django.contrib.auth.models import User
# Crear categorías
tecnologia = Categoria.objects.create(nombre="Tecnología", descripcion="Artículos sobre tecnología")
programacion = Categoria.objects.create(nombre="Programación", descripcion="Tutoriales de programación")
# Crear un usuario (o usar uno existente)
usuario, created = User.objects.get_or_create(username="admin", email="admin@example.com")
if created:
usuario.set_password("password")
usuario.save()
# Crear artículos
articulo1 = Articulo.objects.create(
titulo="Introducción a Django",
contenido="Django es un framework web de alto nivel...",
autor=usuario,
publicado=True
)
# Agregar categorías al artículo
articulo1.categorias.add(tecnologia, programacion)
# Crear un comentario
comentario = Comentario.objects.create(
articulo=articulo1,
autor=usuario,
contenido="¡Gran artículo!",
aprobado=True
)
# Realizar consultas
print("\nArtículos publicados:")
for articulo in Articulo.objects.filter(publicado=True):
print(f"- {articulo.titulo} por {articulo.autor.username}")
print(f" Categorías: {', '.join([c.nombre for c in articulo.categorias.all()])}")
print(f" Comentarios: {articulo.comentarios.count()}")
Has creado un conjunto completo de modelos relacionados para un blog y has realizado operaciones básicas con el ORM de Django. Esto te da una base sólida para trabajar con modelos en tus proyectos.
Comprueba tus conocimientos sobre este módulo respondiendo las siguientes preguntas:
1. ¿Qué clase base deben heredar todos los modelos en Django?
2. ¿Qué tipo de campo usarías para almacenar una cantidad monetaria con precisión fija?
3. ¿Qué opción de on_delete impide que se elimine un objeto si existen objetos relacionados?
4. ¿Qué comando se utiliza para generar archivos de migración basados en los cambios en los modelos?
5. ¿Qué método se utiliza para obtener un objeto único de la base de datos, generando una excepción si no existe?