Skip to main content

Modelos de Datos

User Model

CustomUser Model

# managementApp/modelsfolder/UserModel.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
phone = models.CharField(max_length=20, blank=True, null=True)
document_id = models.CharField(max_length=50, blank=True, null=True)
address = models.TextField(blank=True, null=True)
birth_date = models.DateField(blank=True, null=True)
profile_image = models.URLField(blank=True, null=True)
is_verified = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
return f"{self.first_name} {self.last_name} ({self.email})"

Profile System

class Profile(models.Model):
profile_name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True, null=True)
permissions = models.JSONField(default=dict)
active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.profile_name

class UserProfile(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
assigned_at = models.DateTimeField(auto_now_add=True)

class Meta:
unique_together = ('user', 'profile')

Campos Principales

  • Identificación: username, email, document_id
  • Información Personal: first_name, last_name, phone, address, birth_date
  • Seguridad: password (hasheado con BCrypt), is_verified
  • Metadatos: created_at, updated_at, profile_image

Property Model

PropertyModel

# managementApp/modelsfolder/PropertyModel.py
class PropertyModel(models.Model):
PROPERTY_TYPES = [
('house', 'Casa'),
('apartment', 'Apartamento'),
('commercial', 'Local Comercial'),
('office', 'Oficina'),
('warehouse', 'Bodega'),
]

STATUS_CHOICES = [
('available', 'Disponible'),
('occupied', 'Ocupada'),
('maintenance', 'Mantenimiento'),
('inactive', 'Inactiva'),
]

title = models.CharField(max_length=200)
description = models.TextField()
property_type = models.CharField(max_length=20, choices=PROPERTY_TYPES)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='available')

# Location
address = models.TextField()
city = models.CharField(max_length=100)
state = models.CharField(max_length=100)
country = models.CharField(max_length=100)
postal_code = models.CharField(max_length=20, blank=True)
latitude = models.DecimalField(max_digits=10, decimal_places=8, blank=True, null=True)
longitude = models.DecimalField(max_digits=11, decimal_places=8, blank=True, null=True)

# Characteristics
bedrooms = models.IntegerField(default=1)
bathrooms = models.IntegerField(default=1)
area = models.DecimalField(max_digits=8, decimal_places=2) # m²
floors = models.IntegerField(default=1)
parking_spaces = models.IntegerField(default=0)
balcony = models.BooleanField(default=False)

# Financial
price = models.DecimalField(max_digits=12, decimal_places=2)
currency = models.CharField(max_length=3, default='COP')
deposit = models.DecimalField(max_digits=12, decimal_places=2, default=0)
admin_fee = models.DecimalField(max_digits=10, decimal_places=2, default=0)

# Amenities
amenities = models.JSONField(default=list) # ['wifi', 'ac', 'gym', ...]

# Dates
available_from = models.DateField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
return f"{self.title} - {self.city}"

PropertyImage Model

class PropertyImage(models.Model):
property = models.ForeignKey(PropertyModel, on_delete=models.CASCADE, related_name='images')
image_url = models.URLField()
is_main = models.BooleanField(default=False)
order_index = models.IntegerField(default=0)
uploaded_at = models.DateTimeField(auto_now_add=True)

class Meta:
ordering = ['order_index']

Características Principales

  • Información Básica: title, description, property_type, status
  • Ubicación: address, city, state, coordinates
  • Características Físicas: bedrooms, bathrooms, area, floors
  • Información Financiera: price, deposit, admin_fee
  • Amenidades: JSON field para flexibilidad
  • Imágenes: Relación uno a muchos con PropertyImage

Contract Model

ContractModel

# managementApp/modelsfolder/ContractModel.py
class ContractModel(models.Model):
CONTRACT_TYPES = [
('residential', 'Residencial'),
('commercial', 'Comercial'),
]

STATUS_CHOICES = [
('draft', 'Borrador'),
('pending', 'Pendiente'),
('active', 'Activo'),
('expired', 'Vencido'),
('terminated', 'Terminado'),
]

property = models.ForeignKey(PropertyModel, on_delete=models.CASCADE)
landlord = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='landlord_contracts')
tenant = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='tenant_contracts')
guarantor = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True)

contract_type = models.CharField(max_length=20, choices=CONTRACT_TYPES)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')

# Dates
start_date = models.DateField()
end_date = models.DateField()
signing_date = models.DateTimeField(blank=True, null=True)

# Financial Terms
monthly_rent = models.DecimalField(max_digits=12, decimal_places=2)
deposit = models.DecimalField(max_digits=12, decimal_places=2)
payment_day = models.IntegerField(default=1) # Day of month (1-31)
annual_increase = models.DecimalField(max_digits=5, decimal_places=2, default=0) # Percentage

# Contract Details
contract_text = models.TextField()
terms_and_conditions = models.TextField()
special_clauses = models.TextField(blank=True)

# Digital Signatures
landlord_signature_hash = models.CharField(max_length=255, blank=True)
tenant_signature_hash = models.CharField(max_length=255, blank=True)
guarantor_signature_hash = models.CharField(max_length=255, blank=True)

# Metadata
contract_hash = models.CharField(max_length=255, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
return f"Contrato {self.id} - {self.property.title}"

ContractSignature Model

class ContractSignature(models.Model):
contract = models.ForeignKey(ContractModel, on_delete=models.CASCADE, related_name='signatures')
signer = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
signature_data = models.TextField() # Base64 signature image
signature_hash = models.CharField(max_length=255)
ip_address = models.GenericIPAddressField()
user_agent = models.TextField()
signed_at = models.DateTimeField(auto_now_add=True)

Características del Contrato

  • Partes: property, landlord, tenant, guarantor (opcional)
  • Términos Financieros: monthly_rent, deposit, payment_day
  • Fechas: start_date, end_date, signing_date
  • Contenido: contract_text, terms_and_conditions, special_clauses
  • Firmas Digitales: Hashes de seguridad para cada firma
  • Integridad: contract_hash para verificación de autenticidad

# managementApp/modelsfolder/MenuModel.py
class MenuModel(models.Model):
menu_title = models.CharField(max_length=100)
icon = models.CharField(max_length=50, blank=True, null=True)
path = models.CharField(max_length=200)
parent_menu = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
permissions_required = models.CharField(max_length=200, blank=True, null=True)
order_index = models.IntegerField(default=0)
active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)

class Meta:
ordering = ['order_index', 'menu_title']

def __str__(self):
return self.menu_title

Características del Menú

  • Jerarquía: parent_menu para estructuras anidadas
  • Permisos: permissions_required para control de acceso
  • Orden: order_index para organización visual
  • Metadatos: icon, path, active status

Subscription Model

SubscriptionModel

# managementApp/modelsfolder/SubscriptionModel.py
class SubscriptionModel(models.Model):
STATUS_CHOICES = [
('active', 'Activa'),
('inactive', 'Inactiva'),
('canceled', 'Cancelada'),
('expired', 'Expirada'),
]

user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
stripe_subscription_id = models.CharField(max_length=200, unique=True)
stripe_customer_id = models.CharField(max_length=200)
status = models.CharField(max_length=20, choices=STATUS_CHOICES)
plan_name = models.CharField(max_length=100)
amount = models.DecimalField(max_digits=10, decimal_places=2)
currency = models.CharField(max_length=3, default='COP')
interval = models.CharField(max_length=20) # monthly, yearly
current_period_start = models.DateTimeField()
current_period_end = models.DateTimeField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def __str__(self):
return f"{self.user.email} - {self.plan_name}"

Relaciones y Constraints

Relaciones Principales

# Usuario tiene múltiples propiedades (como propietario)
landlord_contracts = CustomUser.objects.filter(landlord_contracts__isnull=False)

# Usuario puede ser inquilino en múltiples contratos
tenant_contracts = CustomUser.objects.filter(tenant_contracts__isnull=False)

# Propiedad puede tener múltiples contratos en el tiempo
property_contracts = PropertyModel.objects.prefetch_related('contractmodel_set')

# Usuario puede tener múltiples perfiles/roles
user_profiles = CustomUser.objects.prefetch_related('userprofile_set__profile')

Constraints de Integridad

  • Unique Constraints: email único, contract_hash único
  • Foreign Key Constraints: Eliminación en cascada donde apropiado
  • Check Constraints: Validaciones de rangos y valores permitidos
  • Index Optimization: Índices en campos de búsqueda frecuente

Validaciones de Modelo

def clean(self):
# Validación personalizada en PropertyModel
if self.end_date <= self.start_date:
raise ValidationError('End date must be after start date')

if self.monthly_rent <= 0:
raise ValidationError('Monthly rent must be positive')