Django
Tutorial Django: Creación de modelos
Django: Aprende a definir y gestionar clases de modelo, optimizando su uso en la base de datos. Mejora el diseño y rendimiento de tus aplicaciones.
Aprende Django GRATIS y certifícateDefinición de clases de modelo
En Django, los modelos representan las estructuras de datos y se corresponden con tablas en la base de datos. Para definir un modelo, se crea una clase que hereda de django.db.models.Model
, proporcionando así acceso al ORM (Object-Relational Mapping) de Django.
La sintaxis básica para definir una clase de modelo es la siguiente:
from django.db import models
class MiModelo(models.Model):
# Campos del modelo
campo1 = models.TipoDeCampo(parámetros)
campo2 = models.TipoDeCampo(parámetros)
# ...
Cada atributo de la clase es un campo que define una columna en la tabla de la base de datos. Por ejemplo, para crear un modelo Libro
con campos como título
, autor
y fecha_publicación
, se procedería así:
class Libro(models.Model):
título = models.CharField(max_length=200)
autor = models.CharField(max_length=100)
fecha_publicación = models.DateField()
En este ejemplo, CharField
y DateField
son tipos de campo proporcionados por Django que indican el tipo de datos almacenados. El parámetro max_length
en CharField
establece la longitud máxima del texto.
Es recomendable que los nombres de las clases de modelo sean en singular y utilicen estilo CamelCase, como Libro
o Autor
. Los nombres de los campos deben ser en minúsculas y, si constan de varias palabras, separadas por guiones bajos, por ejemplo, fecha_publicación
.
Además de definir campos, las clases de modelo pueden incluir métodos que añaden funcionalidades específicas. Un método común es __str__
, que retorna una representación en cadena del objeto:
from django.db import models
class Libro(models.Model):
título = models.CharField(max_length=200)
autor = models.CharField(max_length=100)
fecha_publicación = models.DateField()
def __str__(self):
return f"{self.título} por {self.autor}"
Este método es útil para identificar instancias del modelo de forma legible cuando se visualizan en el panel de administración o en la consola.
También es posible definir métodos personalizados que realicen operaciones sobre los datos del modelo. Por ejemplo, un método que calcule la antigüedad del libro:
from datetime import date
class Libro(models.Model):
título = models.CharField(max_length=200)
autor = models.CharField(max_length=100)
fecha_publicación = models.DateField()
def __str__(self):
return f"{self.título} por {self.autor}"
def antigüedad(self):
return date.today().year - self.fecha_publicación.year
Los modelos pueden utilizarse para establecer relaciones entre tablas, como relaciones uno a uno, uno a muchos o muchos a muchos. Aunque la definición detallada de estas relaciones se aborda en otra sección, es relevante saber que se implementan mediante campos especiales como ForeignKey
, OneToOneField
o ManyToManyField
.
Por ejemplo, para relacionar un Libro
con una Editorial
:
class Editorial(models.Model):
nombre = models.CharField(max_length=100)
dirección = models.CharField(max_length=200)
def __str__(self):
return self.nombre
class Libro(models.Model):
título = models.CharField(max_length=200)
autor = models.CharField(max_length=100)
fecha_publicación = models.DateField()
editorial = models.ForeignKey(Editorial, on_delete=models.CASCADE)
def __str__(self):
return f"{self.título} por {self.autor}"
En este caso, el campo editorial
establece una relación muchos a uno con el modelo Editorial
. El parámetro on_delete=models.CASCADE
indica que si se elimina una editorial, también se eliminarán los libros asociados.
Es posible utilizar opciones avanzadas al definir campos, como establecer valores por defecto, añadir validaciones personalizadas o utilizar opciones de ayuda para mejorar la documentación:
class Libro(models.Model):
título = models.CharField(max_length=200, help_text="Título completo del libro")
autor = models.CharField(max_length=100)
fecha_publicación = models.DateField(default=date.today)
precio = models.DecimalField(max_digits=5, decimal_places=2, default=0.00)
def __str__(self):
return f"{self.título} por {self.autor}"
Aquí, help_text
proporciona información adicional en el panel de administración, y se establecen valores por defecto para fecha_publicación
y precio
.
Al organizar el código, es buena práctica agrupar modelos relacionados en el mismo archivo models.py
o en módulos separados dentro de la aplicación.
Finalmente, es fundamental recordar que después de definir o modificar las clases de modelo, es necesario crear y aplicar las migraciones correspondientes para actualizar la estructura de la base de datos. Esto se logra utilizando los comandos
python manage.py makemigrations
y
python manage.py migrate
Uso de Meta
y opciones
En Django, cada modelo puede incluir una clase interna llamada Meta
donde se definen opciones y metadatos que afectan al comportamiento del modelo. Estas opciones permiten personalizar aspectos como el nombre de la tabla en la base de datos, el ordenamiento por defecto, restricciones de unicidad y permisos, entre otros.
Para utilizar la clase Meta
, se define dentro de la clase del modelo de la siguiente manera:
from django.db import models
class Libro(models.Model):
título = models.CharField(max_length=200)
autor = models.CharField(max_length=100)
fecha_publicación = models.DateField()
class Meta:
# Opciones y metadatos
pass
Una de las opciones más comunes es verbose_name
y verbose_name_plural
, que establecen un nombre legible para el modelo en singular y plural:
class Meta:
verbose_name = "Libro"
verbose_name_plural = "Libros"
Estas propiedades son útiles en el sitio de administración de Django y en otras interfaces donde se muestra el nombre del modelo.
La opción ordering
permite definir el ordenamiento por defecto de los registros al realizar consultas. Por ejemplo, para ordenar los libros por título de forma ascendente:
class Meta:
ordering = ['título']
Para un orden descendente, se utiliza el prefijo -
:
class Meta:
ordering = ['-fecha_publicación']
Así, los libros se recuperarán desde la fecha más reciente hasta la más antigua.
Si se desea especificar el nombre de la tabla en la base de datos, se utiliza db_table
:
class Meta:
db_table = 'libros'
Por defecto, Django asigna un nombre basado en el nombre de la aplicación y del modelo, pero con db_table
se puede personalizar.
La opción unique_together
establece que una combinación de campos debe ser única en la base de datos. Por ejemplo:
class Meta:
unique_together = ('título', 'autor')
Esto garantiza que no existan dos registros con el mismo título y autor.
Para definir restricciones más complejas, se utilizan constraints
. Una restricción de verificación (CheckConstraint
) asegura que se cumpla una condición específica:
from django.db.models import Q, CheckConstraint
class Meta:
constraints = [
CheckConstraint(check=Q(precio__gte=0), name='precio_no_negativo'),
]
Esta restricción asegura que el campo precio
siempre sea mayor o igual a cero.
Si se necesita que un modelo no cree una tabla en la base de datos y sirva únicamente como clase base para otros modelos, se utiliza la opción abstract = True
:
class BaseModel(models.Model):
creado = models.DateTimeField(auto_now_add=True)
modificado = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
Los modelos que hereden de BaseModel
incorporarán los campos creado
y modificado
, pero no se generará una tabla para BaseModel
.
La opción permissions
permite definir permisos personalizados para el modelo:
class Meta:
permissions = [
("puede_publicar", "Puede publicar libros"),
]
Estos permisos pueden asignarse a usuarios o grupos y son útiles para controlar el acceso a diferentes funcionalidades en la aplicación.
Con default_related_name
, se establece el nombre por defecto para las relaciones inversas en campos de tipo ForeignKey
, ManyToManyField
o OneToOneField
:
class Meta:
default_related_name = 'libros_relacionados'
Esto evita tener que especificar related_name
en cada campo de relación y unifica el acceso desde el modelo relacionado.
La opción get_latest_by
indica el campo que se utilizará al obtener el registro más reciente con los métodos latest()
y earliest()
:
class Meta:
get_latest_by = 'fecha_publicación'
De esta forma, Libro.objects.latest()
retornará el libro con la fecha de publicación más reciente.
Para optimizar el rendimiento de las consultas, se pueden añadir índices mediante la opción indexes
:
from django.db.models import Index
class Meta:
indexes = [
Index(fields=['autor', 'fecha_publicación'], name='autor_fecha_idx'),
]
Esto crea un índice en la base de datos que acelera las búsquedas y filtrados basados en los campos indicados.
Si se requiere definir la relación jerárquica en modelos organizados en proxys o herencias multi-table, se utiliza proxy
y base_manager_name
:
class Meta:
proxy = True
base_manager_name = 'objetos_activos'
Con proxy = True
, se crea un modelo proxy que comparte la misma estructura de base de datos pero puede tener métodos o propiedades adicionales.
La opción managed
controla si Django debe gestionar la creación, modificación y eliminación de la tabla asociada al modelo:
class Meta:
managed = False
Establecer managed = False
es útil cuando se trabaja con vistas o tablas ya existentes en la base de datos que no deben ser manipuladas por Django.
Finalmente, select_on_save
indica si, después de guardar un objeto, se debe volver a cargar desde la base de datos:
class Meta:
select_on_save = True
Esto puede ser útil en casos donde los triggers de la base de datos modifican los datos al guardar.
El uso adecuado de la clase Meta
y sus opciones permite un mayor control y flexibilidad sobre cómo los modelos interactúan con la base de datos y con el framework de Django. Configurar estas opciones de manera consciente contribuirá al buen diseño de modelos y, en consecuencia, a una aplicación más robusta.
Tipos de campos y validaciones
Los campos de modelo en Django determinan el tipo de datos que se almacenarán en la base de datos y ofrecen una interfaz para interactuar con esos datos. Cada campo corresponde a un tipo específico de columna en la base de datos, y Django proporciona una amplia variedad de tipos de campo para cubrir diversas necesidades.
Campos básicos
Entre los campos más utilizados se encuentran:
- CharField: almacena cadenas de texto de longitud limitada. Requiere el parámetro
max_length
, que establece la longitud máxima de la cadena.
nombre = models.CharField(max_length=100)
- TextField: almacena texto de longitud ilimitada. No requiere
max_length
, pero es recomendable utilizarlo cuando se espera almacenar textos largos.
descripción = models.TextField()
- IntegerField: almacena números enteros. Ideal para conteos o identificadores numéricos.
edad = models.IntegerField()
- BooleanField: almacena valores booleanos (
True
oFalse
). Se puede utilizar para banderas o estados binarios.
activo = models.BooleanField(default=True)
- DateField y DateTimeField: almacenan fechas y fechas con hora, respectivamente. Se pueden utilizar los parámetros
auto_now
yauto_now_add
para automatizar el registro de fechas de modificación y creación.
fecha_nacimiento = models.DateField()
último_acceso = models.DateTimeField(auto_now=True)
Campos específicos
Django también ofrece campos más especializados:
- EmailField: almacena direcciones de correo electrónico y aplica una validación básica del formato.
correo_electrónico = models.EmailField(unique=True)
- URLField: almacena URLs y verifica que tengan un formato válido.
sitio_web = models.URLField(blank=True)
- DecimalField: almacena números decimales de precisión fija. Requiere los parámetros
max_digits
(total de dígitos) ydecimal_places
(dígitos después del punto decimal).
precio = models.DecimalField(max_digits=10, decimal_places=2)
- FileField y ImageField: permiten almacenar rutas a archivos y imágenes, respectivamente. Requieren configurar
MEDIA_ROOT
yMEDIA_URL
en el proyecto.
documento = models.FileField(upload_to='documentos/')
foto = models.ImageField(upload_to='fotos/')
- JSONField: almacena datos en formato JSON. Útil para guardar estructuras de datos complejas.
datos_extra = models.JSONField(null=True, blank=True)
Opciones comunes de los campos
Al definir campos, es posible especificar varias opciones:
- null: si se establece en
True
, permite almacenar valores nulos en la base de datos.
segundo_nombre = models.CharField(max_length=50, null=True)
- blank: si es
True
, el campo puede quedar vacío en los formularios. Es importante diferenciarlo denull
, ya queblank
afecta a la validación en niveles más altos.
apodo = models.CharField(max_length=30, blank=True)
- default: define un valor por defecto para el campo si no se especifica uno al crear una instancia.
país = models.CharField(max_length=50, default='España')
- unique: si es
True
, exige que el valor del campo sea único en toda la tabla.
número_identificación = models.CharField(max_length=20, unique=True)
- choices: permite limitar los valores posibles del campo a un conjunto predefinido.
class EstadoCivil(models.TextChoices):
SOLTERO = 'S', 'Soltero'
CASADO = 'C', 'Casado'
DIVORCIADO = 'D', 'Divorciado'
estado_civil = models.CharField(
max_length=1,
choices=EstadoCivil.choices,
default=EstadoCivil.SOLTERO
)
Validaciones personalizadas
Además de las validaciones integradas, es posible añadir validadores personalizados a los campos utilizando el parámetro validators
. Django proporciona un conjunto de validadores en django.core.validators
, y también se pueden definir funciones propias.
Por ejemplo, para validar que un precio sea positivo:
from django.core.validators import MinValueValidator
precio = models.DecimalField(
max_digits=10,
decimal_places=2,
validators=[MinValueValidator(0.01)]
)
Y para crear un validador personalizado:
from django.core.exceptions import ValidationError
def validar_par_impar(valor):
if valor % 2 != 0:
raise ValidationError('%(valor)s no es un número par', params={'valor': valor})
número_par = models.IntegerField(validators=[validar_par_impar])
Uso de verbose_name y help_text
Los campos pueden incluir los parámetros verbose_name y help_text para mejorar la legibilidad y documentación en el sitio de administración y formularios.
usuario = models.CharField(
max_length=50,
verbose_name='Nombre de usuario',
help_text='Introduce tu nombre de usuario único.'
)
Campos personalizados
En situaciones avanzadas, se pueden crear campos personalizados heredando de models.Field
. Esto permite definir nuevos tipos de campo con comportamientos específicos.
class ColorField(models.Field):
description = "Un campo para almacenar colores en formato hexadecimal"
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 7
super().__init__(*args, **kwargs)
def db_type(self, connection):
return 'char(7)'
Luego, se puede usar en el modelo:
color_favorito = ColorField()
Consideraciones sobre las validaciones
Las validaciones se ejecutan al limpiar los datos en formularios y al llamar al método full_clean()
en las instancias de los modelos. No se invocan automáticamente al guardar con save()
. Por ello, es recomendable utilizar full_clean()
antes de guardar si se desea asegurar que los datos cumplen todas las validaciones.
instancia = MiModelo(campo='valor')
instancia.full_clean()
instancia.save()
También es posible implementar el método clean()
en el modelo para realizar validaciones a nivel de instancia.
from django.core.exceptions import ValidationError
class Producto(models.Model):
nombre = models.CharField(max_length=100)
precio = models.DecimalField(max_digits=10, decimal_places=2)
descuento = models.DecimalField(max_digits=4, decimal_places=2)
def clean(self):
if self.descuento > self.precio:
raise ValidationError('El descuento no puede ser mayor que el precio.')
Optimización de modelos
Seleccionar adecuadamente los tipos de campo y configurar las opciones permite optimizar el rendimiento y la integridad de la base de datos. Por ejemplo, limitar la longitud de cadenas con max_length
ayuda a evitar desperdicio de espacio y posibles errores.
Además, utilizar restricciones como unique
, null=False
y validadores garantiza la consistencia de los datos almacenados.
Buen diseño de modelos
Un buen diseño de modelos en Django es esencial para garantizar la eficiencia y mantenibilidad de una aplicación. Al planificar los modelos, es importante considerar no solo las necesidades actuales, sino también cómo evolucionará el proyecto en el futuro.
Aplicar el principio de responsabilidad única asegura que cada modelo represente una entidad específica y tenga una única responsabilidad. Esto facilita la comprensión del código y reduce el acoplamiento entre diferentes partes de la aplicación.
La elección adecuada de los tipos de campo es crucial para mantener la integridad de los datos. Utilizar campos que reflejen con precisión el tipo de información almacenada previene errores y simplifica las validaciones. Por ejemplo, emplear PositiveIntegerField
para valores que nunca serán negativos.
Es recomendable utilizar nombres significativos y consistentes tanto para modelos como para campos. Nombres claros mejoran la legibilidad y facilitan el mantenimiento. Evitar abreviaturas ambiguas y seguir convenciones establecidas es una buena práctica.
Implementar el método __str__
en los modelos proporciona representaciones legibles de las instancias. Esto es útil en el panel de administración y al depurar, ya que muestra información relevante en lugar de referencias de objeto poco informativas.
Considerar el uso de clases base abstractas para compartir campos y comportamientos comunes entre varios modelos. Esto evita la duplicación de código y facilita la implementación de cambios globales. Por ejemplo:
from django.db import models
class TiempoModelo(models.Model):
creado = models.DateTimeField(auto_now_add=True)
modificado = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Artículo(TiempoModelo):
título = models.CharField(max_length=200)
contenido = models.TextField()
En este ejemplo, Artículo
hereda los campos creado
y modificado
de la clase abstracta TiempoModelo
.
Utilizar las opciones Meta correctamente mejora el comportamiento y organización de los modelos. Definir el ordenamiento por defecto, nombres legibles y restricciones contribuye a una base de datos más coherente.
Definir restricciones y criterios de unicidad asegura que los datos almacenados sean válidos y previene inconsistencias. Utilizar unique
, unique_together
y validaciones personalizadas en los modelos es esencial para mantener la integridad de los datos.
Planificar la escalabilidad desde el inicio es fundamental. Anticipar el crecimiento de la cantidad de datos y diseñar modelos que puedan manejar ese aumento sin pérdida de rendimiento es una consideración clave en proyectos de larga duración.
Mantener la lógica de negocio fuera de los modelos promueve una arquitectura más limpia. Los modelos deben enfocarse en representar estructuras de datos, mientras que la lógica compleja se gestiona en otras capas, como servicios o utilidades.
Optimizar el uso de índices en campos que se utilizan frecuentemente en consultas mejora el rendimiento. Definir índices en los campos adecuados acelera las operaciones de búsqueda y filtrado, especialmente en tablas con gran cantidad de registros.
Documentar los modelos y sus campos utilizando help_text
y comentarios en el código facilita la comprensión para otros desarrolladores. Una buena documentación es esencial para el trabajo en equipo y el mantenimiento futuro.
Organizar los modelos en módulos o aplicaciones según su funcionalidad mejora la estructura del proyecto. Una separación lógica facilita la navegación por el código y permite escalabilidad en proyectos más complejos.
Implementar managers personalizados permite encapsular consultas comunes y añadir funcionalidad adicional a los modelos. Por ejemplo:
class ActivoManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(activo=True)
class Producto(models.Model):
nombre = models.CharField(max_length=100)
activo = models.BooleanField(default=True)
objects = models.Manager() # Manager por defecto
activos = ActivoManager() # Manager personalizado
Con este esquema, Producto.activos.all()
retornará únicamente los productos activos, simplificando el acceso a datos relevantes.
Gestionar adecuadamente las zonas horarias es esencial en aplicaciones que manejan fechas y horas. Configurar USE_TZ
en settings.py
y utilizar campos conscientes de zona horaria evita discrepancias y errores en el manejo temporal.
Evitar dependencias circulares entre modelos y aplicaciones previene problemas de importación y conflictos al definir relaciones. Una arquitectura bien planeada minimiza estas dependencias, facilitando la extensibilidad del proyecto.
Aplicar el principio DRY ("Don't Repeat Yourself") en el diseño de modelos evita la redundancia. Centralizar lógica y reutilizar componentes promueve un código más limpio y reduce el riesgo de errores.
Todas las lecciones de Django
Accede a todas las lecciones de Django y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Django
Introducción Y Entorno
Instalación Y Configuración Django Con Venv
Introducción Y Entorno
Arquitectura De Un Proyecto Django
Introducción Y Entorno
Base De Datos Mysql En Django
Modelos Y Base De Datos
Creación De Modelos
Modelos Y Base De Datos
Asociaciones De Modelos
Modelos Y Base De Datos
Migraciones
Modelos Y Base De Datos
Operaciones Crud Y Consultas
Modelos Y Base De Datos
Enrutamiento Básico
Vistas Y Plantillas
Plantillas Con Django Template Language
Vistas Y Plantillas
Vistas Basadas En Funciones
Vistas Y Plantillas
Vistas Basadas En Clases
Vistas Y Plantillas
Middlewares
Vistas Y Plantillas
Form Vs Modelform
Formularios
Procesamiento De Formularios
Formularios
Subida De Archivos
Formularios
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender el concepto de modelos en Django.
- Crear y definir clases de modelo y sus campos.
- Configurar opciones avanzadas utilizando la clase Meta.
- Implementar validaciones y restricciones personalizadas.
- Establecer relaciones entre modelos.
- Optimizar el diseño de modelos para mejorar el rendimiento y la integridad de datos.