Saltar a contenido

dorm.contrib.tenants_row

Row-level multi-tenancy: TenantModel + current_tenant().

Recetas en Row-level multi-tenancy.

API

dorm.contrib.tenants_row.TenantModel

Bases: Model

Base class for row-level tenant-scoped models.

Adds:

  • tenant_id :class:~dorm.fields.CharField (override by redeclaring in the subclass with a different field type if you prefer UUIDField or IntegerField).
  • objects — :class:TenantManager, scoped reads/writes.
  • unscoped — :class:_UnscopedManager, the escape hatch.
  • :meth:save / :meth:asave auto-fill tenant_id from the active tenant when the field is unset.

Override Meta.tenant_field to change the column name (e.g. "org_id" if your domain uses orgs not tenants).

dorm.contrib.tenants_row.TenantManager

Bases: Manager

Manager that filters every queryset by the active tenant.

On each call to :meth:get_queryset we read the active tenant from the contextvar and inject filter(<tenant_field>=...). When no tenant is active the manager raises :class:NoActiveTenantError rather than silently returning rows from every tenant.

The column name used for the filter is read from the model's Meta.tenant_field if present (default "tenant_id"), so user code that overrides the column doesn't have to subclass the manager too.

dorm.contrib.tenants_row.current_tenant(tenant_id: Any)

Pin tenant_id as the active tenant for the surrounding block.

Per-task (asyncio) and per-thread context — does not bleed between requests. Nested calls stack: the inner pin wins for its own scope, the outer pin is restored on exit.

dorm.contrib.tenants_row.get_active_tenant() -> Any | None

Return the currently-pinned tenant id, or None.

dorm.contrib.tenants_row.NoActiveTenantError

Bases: RuntimeError

Raised when a query against a tenant-scoped model runs without an active tenant.

Silent fallback to "no filter" would leak rows across tenants — the worst kind of multi-tenant bug. The explicit failure forces every call site to either set a tenant or use :attr:TenantModel.unscoped deliberately.