CreaRack-SL

SaaSModule · Modelo core

Entidadactiveverificado 2026-04-21#core#model#saas#gating

SaaSModule · Modelo core

Propósito

SaaSModule es el registro de módulos de aplicación que pueden habilitarse o deshabilitarse por organización. Cada instancia representa una funcionalidad discreta (racks, network-maps, digital-signage, etc.) identificada por un slug único. El middleware ModuleGatingMiddleware consulta este modelo en cada request para decidir si una URL está accesible. Los módulos con is_core=True están siempre activos sin importar el plan.

Contrato

Campos:

CampoTipoNotas
slugSlugField(50, unique)Identificador canónico usado en registry y middleware
nameCharField(100)Nombre legible para admin
descriptionTextField(blank)Descripción opcional
url_prefixesJSONField(default=list)Referencia documental; la fuente de verdad runtime es module_registry.URL_TO_MODULE
is_coreBooleanField(default=False)Si True, acceso incondicional
sort_orderIntegerField(default=0)Orden de presentación

Meta: ordering = ["sort_order", "name"], verbose_name = "Module". Sin manager personalizado.

Nota: ModulePermission NO tiene FK directo a SaaSModule. Almacena permisos granulares en un JSONField libre ({"scope": "level"}), sin relación de BD.

Dependencias entrantes

ArchivoUso
core/models.py:110 (Organization.get_active_modules)SaaSModule.objects.filter(is_core=True).values_list("slug", flat=True)
core/middleware/module_gating.pyInvoca org.get_active_modules() en cada request no exento
core/api/signup.pyAl crear organización añade módulos core a extra_modules
core/admin.pySaaSModuleAdmin y OrganizationAdmin.save_related sincronizan core modules
tests/conftest.py, tests/api/test_tenant_isolation.pyEnlaza todos los módulos a organizaciones de test

Dependencias salientes

  • Plan.modules — M2M inversa (related_name="plans"). Un módulo puede estar en cero, uno o varios planes.
  • Organization.extra_modules — M2M inversa (related_name="extra_organizations") para módulos à la carte o core pre-asignados en signup.
  • core.module_registry.URL_TO_MODULE — diccionario estático prefijo→slug con longest-prefix-match vía resolve_module(path).

Ejemplos

# Flujo gating en ModuleGatingMiddleware (core/middleware/module_gating.py:62-79)
active = org.get_active_modules()
request._active_modules = active

required_module = resolve_module(path)
if required_module and required_module not in active:
    if path.startswith("/api/"):
        return JsonResponse({"error": f"Module '{required_module}' is not available"}, status=403)
    return render(request, "errors/module_disabled.html", {"module_slug": required_module}, status=403)

# Construcción del set activo (core/models.py:110-112)
active = set(SaaSModule.objects.filter(is_core=True).values_list("slug", flat=True))
active.update(self.extra_modules.values_list("slug", flat=True))
return active
  • entity--core--model--plan — contiene la M2M Plan.modules → SaaSModule.
  • entity--core--model--organization — consume vía extra_modules y get_active_modules().
  • concept--saas--module-gating — patrón arquitectónico implementado por middleware + registry.

Véase también

  • [[entity—core—model—plan]]
  • [[entity—core—model—organization]]
  • [[concept—saas—module-gating]]