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 preferUUIDFieldorIntegerField).objects— :class:TenantManager, scoped reads/writes.unscoped— :class:_UnscopedManager, the escape hatch.- :meth:
save/ :meth:asaveauto-filltenant_idfrom 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.