Introducción a los Modelos¶
El Sistema A3 utiliza Django ORM para modelar la estructura de datos. Esta sección documenta los modelos principales del sistema.
Convenciones¶
Nombres de Modelos¶
- Singular:
Casa,Cliente,Ticket(noCasas,Clientes) - CamelCase:
PreInventario,SolicitudRecurso - Sin prefijos:
Casa(noModeloCasa)
Campos Comunes¶
Casi todos los modelos incluyen:
# Auditoría
fecha_creacion = models.DateTimeField(auto_now_add=True)
fecha_modificacion = models.DateTimeField(auto_now=True)
usuario_creacion = models.ForeignKey(User, on_delete=models.PROTECT)
# Control
activo = models.BooleanField(default=True)
cancelado = models.BooleanField(default=False)
Meta Options¶
class Meta:
db_table = 'nombre_tabla'
verbose_name = 'Nombre Singular'
verbose_name_plural = 'Nombre Plural'
ordering = ['-fecha_creacion']
indexes = [
models.Index(fields=['campo_frecuente']),
]
Patrones de Diseño¶
1. Herencia de Modelos¶
Abstract Base Classes:
class AnexoBase(models.Model):
archivo = models.FileField(upload_to='anexos/')
tipo_anexo = models.CharField(max_length=50)
fecha_creacion = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
class AnexoCasa(AnexoBase):
casa = models.ForeignKey('Casa', on_delete=models.CASCADE)
2. Relaciones¶
One-to-Many:
class Casa(models.Model):
proyecto = models.CharField(max_length=100)
class AnexoCasa(models.Model):
casa = models.ForeignKey(Casa, on_delete=models.CASCADE,
related_name='anexos')
Many-to-Many:
class Ticket(models.Model):
colaboradores = models.ManyToManyField(User, related_name='tickets_colaborando')
One-to-One:
class User(models.Model):
pass # Django built-in
class Perfil(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
3. Choices¶
class Apartado(models.Model):
TIPO_VENTA_CHOICES = [
('CREDITO', 'Crédito'),
('CONTADO', 'Contado'),
('C90', 'C90'),
('OTRO', 'Otro'),
]
tipo_venta = models.CharField(
max_length=20,
choices=TIPO_VENTA_CHOICES,
default='CREDITO'
)
4. Métodos Personalizados¶
class Apartado(models.Model):
precio_final = models.DecimalField(max_digits=12, decimal_places=2)
enganche_pagado = models.DecimalField(max_digits=12, decimal_places=2)
def saldo_pendiente(self):
return self.precio_final - self.enganche_pagado
def __str__(self):
return f"Apartado {self.id} - {self.casa.clave}"
5. Managers Personalizados¶
class CasaManager(models.Manager):
def disponibles(self):
return self.filter(disponible=True, activo=True)
def por_plaza(self, plaza):
return self.filter(plaza=plaza)
class Casa(models.Model):
# ...campos...
objects = CasaManager()
# Uso
casas_disponibles = Casa.objects.disponibles()
casas_mty = Casa.objects.por_plaza('MONTERREY')
Validaciones¶
A Nivel de Campo¶
from django.core.validators import MinValueValidator, MaxValueValidator
class Casa(models.Model):
precio_venta = models.DecimalField(
max_digits=12,
decimal_places=2,
validators=[MinValueValidator(0)]
)
A Nivel de Modelo¶
from django.core.exceptions import ValidationError
class Casa(models.Model):
precio_venta = models.DecimalField(max_digits=12, decimal_places=2)
precio_contado = models.DecimalField(max_digits=12, decimal_places=2)
def clean(self):
if self.precio_contado > self.precio_venta:
raise ValidationError('El precio de contado no puede ser mayor al de venta')
Signals¶
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User)
def crear_perfil(sender, instance, created, **kwargs):
if created:
Perfil.objects.create(user=instance)
Queries Comunes¶
# Select Related (One-to-One, ForeignKey)
casas = Casa.objects.select_related('usuario_creacion').all()
# Prefetch Related (Many-to-Many, Reverse ForeignKey)
casas = Casa.objects.prefetch_related('anexos').all()
# Filtros
casas = Casa.objects.filter(
plaza='MONTERREY',
disponible=True,
precio_venta__lte=1000000
)
# Anotaciones
from django.db.models import Count
casas = Casa.objects.annotate(num_anexos=Count('anexos'))
Migraciones¶
# Crear migraciones
python manage.py makemigrations
# Ver SQL
python manage.py sqlmigrate inventario 0001
# Aplicar
python manage.py migrate
# Revertir
python manage.py migrate inventario 0001
Documentar con Docstrings¶
class Casa(models.Model):
"""
Representa una vivienda en el inventario de A Terceros.
Attributes:
clave (str): Identificador único de la casa (formato: XX-XXXXX)
proyecto (str): Nombre del fraccionamiento
precio_venta (Decimal): Precio de venta a crédito
disponible (bool): Si está disponible para venta
Example:
>>> casa = Casa.objects.create(
... clave='01-00123',
... proyecto='Hacienda del Bosque',
... precio_venta=850000
... )
"""
pass
Ver Modelos Específicos¶
Para más información sobre Django Models, consulta la documentación oficial.