Saltar a contenido

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 (no Casas, Clientes)
  • CamelCase: PreInventario, SolicitudRecurso
  • Sin prefijos: Casa (no ModeloCasa)

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.