API Reference¶
Auto-generated from source code docstrings.
Top-Level Exports¶
The most common symbols are available directly from django_rls_tenants:
from django_rls_tenants import (
AddM2MRLSPolicy,
NoTenantContextError,
RLSConfigurationError,
RLSConstraint,
RLSM2MConstraint,
RLSManager,
RLSProtectedModel,
RLSTenantError,
RLSTenantMiddleware,
TenantQuerySet,
TenantUser,
admin_context,
get_current_tenant_id,
get_rls_context_active,
reset_current_tenant_id,
reset_rls_context_active,
set_current_tenant_id,
set_rls_context_active,
tenant_context,
with_rls_context,
)
Exceptions¶
Custom exception hierarchy for precise error handling. All exceptions live in
django_rls_tenants.exceptions and are re-exported from the top-level package.
django_rls_tenants.exceptions.RLSTenantError
¶
Bases: Exception
Base exception for all django-rls-tenants errors.
Catch this to handle any error raised by the library.
django_rls_tenants.exceptions.NoTenantContextError
¶
Bases: RLSTenantError
Query or context operation attempted without an active tenant context.
Raised when STRICT_MODE=True and a queryset evaluation is attempted
without an active tenant_context(), admin_context(),
for_user(), or RLSTenantMiddleware context.
Also raised by tenant_context() and _resolve_user_guc_vars()
when a non-admin user has rls_tenant_id=None.
django_rls_tenants.exceptions.RLSConfigurationError
¶
Bases: RLSTenantError
Invalid or missing RLS configuration.
Raised when a required configuration key is missing from
settings.RLS_TENANTS or when a configuration value is invalid.
RLS Layer¶
Generic PostgreSQL Row-Level Security primitives. This layer has zero imports
from tenants/.
GUC Helpers¶
django_rls_tenants.rls.guc.set_guc
¶
Set a PostgreSQL session variable (GUC).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Variable name (e.g., |
required |
value
|
str
|
Variable value as string. |
required |
is_local
|
bool
|
If |
False
|
using
|
str
|
Database alias. Default: |
'default'
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
RuntimeError
|
If |
Source code in django_rls_tenants/rls/guc.py
django_rls_tenants.rls.guc.get_guc
¶
Get a PostgreSQL session variable value.
Returns:
| Type | Description |
|---|---|
str | None
|
The variable value, or |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in django_rls_tenants/rls/guc.py
django_rls_tenants.rls.guc.clear_guc
¶
Clear a GUC variable by setting it to an empty string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Variable name to clear. |
required |
is_local
|
bool
|
If |
False
|
using
|
str
|
Database alias. Default: |
'default'
|
Source code in django_rls_tenants/rls/guc.py
Constraints¶
django_rls_tenants.rls.constraints.RLSConstraint
¶
RLSConstraint(
*,
field: str,
name: str,
guc_tenant_var: str = "rls.current_tenant",
guc_admin_var: str = "rls.is_admin",
tenant_pk_type: str = "int",
extra_bypass_flags: list[str] | None = None,
)
Bases: BaseConstraint
Django constraint that generates PostgreSQL RLS policies during migrations.
When Django applies a migration containing this constraint, it:
- Enables RLS on the table (
ALTER TABLE ... ENABLE ROW LEVEL SECURITY). - Forces RLS for the table owner (
ALTER TABLE ... FORCE ROW LEVEL SECURITY). - Creates an isolation policy with configurable
USINGandWITH CHECK.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
field
|
str
|
FK field name for tenant identification (e.g., |
required |
name
|
str
|
Constraint name. Supports |
required |
guc_tenant_var
|
str
|
GUC variable holding the current tenant ID.
Default: |
'rls.current_tenant'
|
guc_admin_var
|
str
|
GUC variable for admin bypass.
Default: |
'rls.is_admin'
|
tenant_pk_type
|
str
|
SQL cast type for tenant PK.
Default: |
'int'
|
extra_bypass_flags
|
list[str] | None
|
Additional GUC variables that bypass the |
None
|
Source code in django_rls_tenants/rls/constraints.py
constraint_sql
¶
No inline constraint SQL; defer RLS DDL to after CREATE TABLE.
Django calls constraint_sql during CREATE TABLE for inline
constraints. RLS policies require the table to exist first, so we
defer the actual DDL and return an empty string (filtered out by
Django's if statement guard).
Source code in django_rls_tenants/rls/constraints.py
create_sql
¶
create_sql(
model: type[Model] | None,
schema_editor: BaseDatabaseSchemaEditor | None,
) -> Statement
Generate SQL to enable RLS and create the isolation policy.
Source code in django_rls_tenants/rls/constraints.py
remove_sql
¶
remove_sql(
model: type[Model] | None,
schema_editor: BaseDatabaseSchemaEditor | None,
) -> Statement
Generate SQL to drop the policy and disable RLS.
Source code in django_rls_tenants/rls/constraints.py
validate
¶
validate(
model: type[Model],
instance: Any,
exclude: Any = None,
using: str | None = None,
) -> None
No-op: RLS is enforced at the database level, not in Django validation.
deconstruct
¶
Return a 3-tuple for Django's migration serializer.
Source code in django_rls_tenants/rls/constraints.py
django_rls_tenants.rls.constraints.RLSM2MConstraint
¶
RLSM2MConstraint(
*,
name: str,
from_model: str,
to_model: str,
from_fk: str,
to_fk: str,
from_tenant_fk: str | None = "tenant",
to_tenant_fk: str | None = "tenant",
guc_tenant_var: str = "rls.current_tenant",
guc_admin_var: str = "rls.is_admin",
tenant_pk_type: str = "int",
)
Bases: BaseConstraint
Constraint generating subquery-based RLS policies for M2M join tables.
Unlike RLSConstraint which checks a direct {field}_id column,
this generates policies that verify both FK references point to rows
belonging to the current tenant via IN (SELECT ...) subqueries.
For join tables where only one side is RLS-protected, only that side's FK is checked. For both sides protected, both FKs are checked.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Constraint name. |
required |
from_model
|
str
|
Dotted path to the "from" model (e.g., |
required |
to_model
|
str
|
Dotted path to the "to" model (e.g., |
required |
from_fk
|
str
|
FK column name for the "from" side (e.g., |
required |
to_fk
|
str
|
FK column name for the "to" side (e.g., |
required |
from_tenant_fk
|
str | None
|
Tenant FK field name on the "from" model,
or |
'tenant'
|
to_tenant_fk
|
str | None
|
Tenant FK field name on the "to" model,
or |
'tenant'
|
guc_tenant_var
|
str
|
GUC variable for current tenant.
Default: |
'rls.current_tenant'
|
guc_admin_var
|
str
|
GUC variable for admin bypass.
Default: |
'rls.is_admin'
|
tenant_pk_type
|
str
|
SQL cast type for tenant PK.
Default: |
'int'
|
Source code in django_rls_tenants/rls/constraints.py
constraint_sql
¶
No inline constraint SQL; defer RLS DDL to after CREATE TABLE.
Source code in django_rls_tenants/rls/constraints.py
create_sql
¶
create_sql(
model: type[Model] | None,
schema_editor: BaseDatabaseSchemaEditor | None,
) -> Statement
Generate SQL to enable RLS and create the M2M isolation policy.
Source code in django_rls_tenants/rls/constraints.py
remove_sql
¶
remove_sql(
model: type[Model] | None,
schema_editor: BaseDatabaseSchemaEditor | None,
) -> Statement
Generate SQL to drop the M2M policy and disable RLS.
Source code in django_rls_tenants/rls/constraints.py
validate
¶
validate(
model: type[Model],
instance: Any,
exclude: Any = None,
using: str | None = None,
) -> None
No-op: RLS is enforced at the database level, not in Django validation.
deconstruct
¶
Return a 3-tuple for Django's migration serializer.
Source code in django_rls_tenants/rls/constraints.py
Migration Operations¶
django_rls_tenants.operations.AddM2MRLSPolicy
¶
AddM2MRLSPolicy(
m2m_table: str,
from_model: str,
to_model: str,
from_fk: str,
to_fk: str,
from_tenant_fk: str | None = "tenant",
to_tenant_fk: str | None = "tenant",
guc_tenant_var: str = "rls.current_tenant",
guc_admin_var: str = "rls.is_admin",
tenant_pk_type: str = "int",
)
Bases: Operation
Migration operation to add an RLS policy to an M2M through table.
Generates and executes subquery-based CREATE POLICY SQL that
checks both FK sides of the M2M join table belong to the current
tenant. Supports tables where only one side is RLS-protected.
This operation is reversible: database_backwards drops the policy
and disables RLS on the table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
m2m_table
|
str
|
The database table name of the M2M through table. |
required |
from_model
|
str
|
Dotted path to the "from" model (e.g., |
required |
to_model
|
str
|
Dotted path to the "to" model (e.g., |
required |
from_fk
|
str
|
FK column on the through table for the "from" side. |
required |
to_fk
|
str
|
FK column on the through table for the "to" side. |
required |
from_tenant_fk
|
str | None
|
Tenant FK on the "from" model, or |
'tenant'
|
to_tenant_fk
|
str | None
|
Tenant FK on the "to" model, or |
'tenant'
|
guc_tenant_var
|
str
|
GUC variable for current tenant.
Default: |
'rls.current_tenant'
|
guc_admin_var
|
str
|
GUC variable for admin bypass.
Default: |
'rls.is_admin'
|
tenant_pk_type
|
str
|
SQL cast type for tenant PK.
Default: |
'int'
|
Source code in django_rls_tenants/operations.py
state_forwards
¶
database_forwards
¶
database_forwards(
app_label: str,
schema_editor: BaseDatabaseSchemaEditor,
from_state: ProjectState,
to_state: ProjectState,
) -> None
Create the M2M RLS policy.
Source code in django_rls_tenants/operations.py
database_backwards
¶
database_backwards(
app_label: str,
schema_editor: BaseDatabaseSchemaEditor,
from_state: ProjectState,
to_state: ProjectState,
) -> None
Drop the M2M RLS policy and disable RLS.
Source code in django_rls_tenants/operations.py
describe
¶
deconstruct
¶
Return args for Django's migration serializer.
Source code in django_rls_tenants/operations.py
Context Managers¶
django_rls_tenants.rls.context.rls_context
¶
rls_context(
variables: dict[str, str],
*,
is_local: bool = False,
using: str = "default",
) -> Iterator[None]
Set multiple GUC variables for the duration of a block.
Saves and restores previous values on exit (supports nesting).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
variables
|
dict[str, str]
|
Dict of GUC variable names to values. |
required |
is_local
|
bool
|
If |
False
|
using
|
str
|
Database alias. Default: |
'default'
|
Source code in django_rls_tenants/rls/context.py
django_rls_tenants.rls.context.bypass_flag
¶
Temporarily set a GUC bypass flag to 'true'.
Saves and restores previous value on exit (supports nesting).
Usage::
with bypass_flag("rls.is_login_request"):
user = User.objects.get(email=email)
Source code in django_rls_tenants/rls/context.py
Tenants Layer¶
Django multitenancy built on top of the rls/ primitives.
Configuration¶
django_rls_tenants.tenants.conf.RLSTenantsConfig
¶
Read library configuration from settings.RLS_TENANTS.
All settings live under a single dict::
RLS_TENANTS = {
"TENANT_MODEL": "myapp.Tenant", # Required
"DATABASES": ["default"], # Default: ["default"]
"GUC_PREFIX": "rls", # Default: "rls"
"STRICT_MODE": False, # Default: False
"TENANT_FK_FIELD": "tenant", # Default: "tenant"
"USER_PARAM_NAME": "as_user", # Default: "as_user"
"TENANT_PK_TYPE": "int", # Default: "int"
"USE_LOCAL_SET": False, # Default: False
}
Source code in django_rls_tenants/tenants/conf.py
TENANT_FK_FIELD
property
¶
FK field name on RLSProtectedModel. Default: "tenant".
USER_PARAM_NAME
property
¶
Parameter name @with_rls_context looks for. Default: "as_user".
STRICT_MODE
property
¶
Raise on queries without tenant context. Default: False.
When enabled, TenantQuerySet evaluation methods raise
NoTenantContextError if no RLS context is active (no
tenant_context(), admin_context(), for_user(),
or RLSTenantMiddleware).
DATABASES
property
¶
Database aliases to set GUCs on. Default: ["default"].
In multi-database setups (e.g., read replicas), add all aliases that serve RLS-protected queries::
RLS_TENANTS = {
"DATABASES": ["default", "replica"],
}
Models¶
django_rls_tenants.tenants.models.RLSProtectedModel
¶
Bases: Model
Abstract base model for tenant-scoped models.
Provides:
- A
tenantForeignKey added dynamically via theclass_preparedsignal (target read fromRLS_TENANTS["TENANT_MODEL"]). RLSManageras the default manager (withfor_user()).RLSConstraintinMeta.constraints(generates RLS policy).
Usage::
class Order(RLSProtectedModel):
product = models.CharField(max_length=255)
amount = models.DecimalField(...)
class Meta(RLSProtectedModel.Meta):
db_table = "order"
To customize the tenant FK (e.g., nullable for admin users),
declare the field directly on your model -- the class_prepared
handler will not add a duplicate::
class User(AbstractUser, RLSProtectedModel):
tenant = models.ForeignKey(
Tenant, on_delete=models.CASCADE,
null=True, blank=True,
)
Managers¶
django_rls_tenants.tenants.managers.TenantQuerySet
¶
Bases: QuerySet
QuerySet that sets RLS GUC variables at evaluation time.
Stores the user reference from for_user() and defers GUC setup
to _fetch_all(), ensuring lazy querysets work correctly with RLS.
When auto-scope is active (via tenant_context() or middleware),
select_related() automatically adds WHERE related.tenant_id = X
for joined RLS-protected tables, enabling index usage on both sides
of the join.
.. warning:: Limitation of for_user() GUC management
GUC variables are only set during ``_fetch_all()`` (iteration).
QuerySet methods that bypass ``_fetch_all()`` — such as
``count()``, ``exists()``, ``aggregate()``, ``update()``,
``delete()``, and ``iterator()`` — will **not** have GUC
variables set by ``for_user()``.
For non-admin users this is safe because ``for_user()`` also
adds a Django ORM ``WHERE tenant_id = X`` filter. For **admin
users** (``is_tenant_admin=True``), no ORM filter is applied,
so these methods run against whatever GUC state the connection
already has.
For non-middleware contexts (Celery tasks, management commands),
use ``tenant_context()`` or ``admin_context()`` instead, which
set GUCs at the connection level for the entire block.
Source code in django_rls_tenants/tenants/managers.py
for_user
¶
Scope this queryset to the given user's tenant.
For admin users: returns all rows (RLS admin bypass at eval time). For tenant users: returns rows matching the user's tenant.
The queryset remains lazy and chainable. GUC variables are set
when the queryset is evaluated, not when for_user() is called.
.. warning::
GUC variables are only set during iteration (``_fetch_all``).
Methods like ``count()``, ``exists()``, ``aggregate()``,
``update()``, ``delete()``, and ``iterator()`` bypass
``_fetch_all`` and will **not** have GUC variables set.
For admin users this means those methods run without GUC
protection. Use ``tenant_context()`` or ``admin_context()``
for full coverage in non-middleware contexts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
as_user
|
TenantUser
|
User object satisfying the |
required |
Source code in django_rls_tenants/tenants/managers.py
select_related
¶
Override to add tenant filters on joined RLS-protected tables.
When a tenant context is active (or for_user() was called),
adds WHERE related.tenant_id = X for each explicitly named
relation that targets an RLS-protected model. This enables
PostgreSQL to use composite indexes on joined tables instead of
relying solely on per-row RLS current_setting() evaluation.
Falls back to super().select_related() when no tenant scope
is active or when called with no arguments (select-all mode).
Handles select_related(False) (Django 5.x) and
select_related(None) (Django 6.0+) for clearing without
adding tenant filters.
Source code in django_rls_tenants/tenants/managers.py
count
¶
exists
¶
aggregate
¶
update
¶
delete
¶
iterator
¶
bulk_create
¶
bulk_create(
objs: Any,
batch_size: int | None = None,
ignore_conflicts: bool = False,
update_conflicts: bool = False,
update_fields: Any = None,
unique_fields: Any = None,
) -> list[Any]
Guard bulk_create() with strict mode check.
Source code in django_rls_tenants/tenants/managers.py
bulk_update
¶
Guard bulk_update() with strict mode check.
Source code in django_rls_tenants/tenants/managers.py
get
¶
first
¶
django_rls_tenants.tenants.managers.RLSManager
¶
Bases: Manager
Manager for RLS-protected models.
Provides for_user() for scoped queries and
prepare_tenant_in_model_data() for resolving tenant FKs.
get_queryset
¶
Return a TenantQuerySet instance, auto-scoped if a tenant context is active.
When tenant_context(), admin_context(), or RLSTenantMiddleware
has set a current tenant ID, the queryset is automatically filtered by
WHERE tenant_id = X. This enables PostgreSQL to use composite indexes
instead of relying solely on RLS current_setting() calls.
Source code in django_rls_tenants/tenants/managers.py
for_user
¶
prepare_tenant_in_model_data
¶
Resolve a raw tenant ID for model creation.
If model_data contains a raw tenant ID (int/str) under
the configured FK field name, sets the FK column directly
({field}_id) to avoid a SELECT query. Allows passing
tenant=42 in creation data without N+1 overhead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_data
|
dict[str, Any]
|
Dict of field names to values. |
required |
as_user
|
TenantUser
|
User for context (unused here but part of API). |
required |
Source code in django_rls_tenants/tenants/managers.py
Context Managers¶
django_rls_tenants.tenants.context.tenant_context
¶
Set RLS context to a specific tenant. Supports nesting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant_id
|
int | str
|
The tenant PK to scope queries to. |
required |
using
|
str
|
Database alias. Default: |
'default'
|
Raises:
| Type | Description |
|---|---|
NoTenantContextError
|
If |
Source code in django_rls_tenants/tenants/context.py
django_rls_tenants.tenants.context.admin_context
¶
Set RLS context to admin mode. Supports nesting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
using
|
str
|
Database alias. Default: |
'default'
|
Source code in django_rls_tenants/tenants/context.py
django_rls_tenants.tenants.context.with_rls_context
¶
with_rls_context(
func: Callable[..., Any] | None = None,
*,
user_param: str | None = None,
) -> Callable[..., Any]
Decorator that extracts a user argument and sets RLS context.
Can be used bare or with an explicit user_param::
@with_rls_context
def my_view(request, as_user): ...
@with_rls_context(user_param="current_user")
def my_view(request, current_user): ...
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
func
|
Callable[..., Any] | None
|
The function to decorate (when used without parentheses). |
None
|
user_param
|
str | None
|
Override the parameter name to look for. Defaults to
|
None
|
When the user argument is None, logs a warning and proceeds
without context (fail-closed: RLS blocks all access).
Source code in django_rls_tenants/tenants/context.py
State¶
Tenant ID¶
django_rls_tenants.tenants.state.get_current_tenant_id
¶
Return the current tenant ID, or None if no tenant context is active.
Returns:
| Type | Description |
|---|---|
int | str | None
|
The tenant ID set by the innermost active |
int | str | None
|
middleware, or |
Source code in django_rls_tenants/tenants/state.py
django_rls_tenants.tenants.state.set_current_tenant_id
¶
Set the current tenant ID for automatic query scoping.
Returns a token that can be passed to reset_current_tenant_id()
to restore the previous value (for nesting support).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant_id
|
int | str | None
|
The tenant PK, or |
required |
Returns:
| Type | Description |
|---|---|
Token[int | str | None]
|
A |
Source code in django_rls_tenants/tenants/state.py
django_rls_tenants.tenants.state.reset_current_tenant_id
¶
Restore the previous tenant ID using a token from set_current_tenant_id().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
Token[int | str | None]
|
The token returned by the corresponding |
required |
Source code in django_rls_tenants/tenants/state.py
RLS Context Active (Strict Mode)¶
django_rls_tenants.tenants.state.get_rls_context_active
¶
Return whether an RLS context is currently active.
An RLS context is active when tenant_context(),
admin_context(), or RLSTenantMiddleware has established
a context for the current execution scope. Used by strict mode
to distinguish "no context" from "admin context".
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in django_rls_tenants/tenants/state.py
django_rls_tenants.tenants.state.set_rls_context_active
¶
Set the RLS context active flag.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
active
|
bool
|
Whether an RLS context is active. |
required |
Returns:
| Type | Description |
|---|---|
Token[bool]
|
A |
Source code in django_rls_tenants/tenants/state.py
django_rls_tenants.tenants.state.reset_rls_context_active
¶
Restore the previous RLS context active state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
Token[bool]
|
The token returned by |
required |
Middleware¶
django_rls_tenants.tenants.middleware.RLSTenantMiddleware
¶
Bases: MiddlewareMixin
Set RLS context for each authenticated request.
For authenticated users: sets tenant_context or admin_context
based on the user's TenantUser protocol implementation.
For unauthenticated requests: no context is set. RLS policies block all access to protected tables (fail-closed).
This is API-agnostic -- works identically for REST, GraphQL, Django views, or any other request handler.
Add to MIDDLEWARE::
MIDDLEWARE = [
...
"django_rls_tenants.tenants.middleware.RLSTenantMiddleware",
]
process_request
¶
Set GUC variables on all configured databases.
Iterates over RLS_TENANTS["DATABASES"] and sets GUCs on each
alias. If setting GUCs on one database fails, clears all GUCs on
databases that were already set, then re-raises.
Source code in django_rls_tenants/tenants/middleware.py
process_response
¶
Clear GUC variables and auto-scope state to prevent cross-request leaks.
Source code in django_rls_tenants/tenants/middleware.py
process_exception
¶
Clear RLS state on unhandled exceptions to prevent ContextVar leaks.
Without this, a view exception that prevents process_response
from running would leave the ContextVar set for the remainder of
the thread (WSGI) or async task (ASGI).
Source code in django_rls_tenants/tenants/middleware.py
Types¶
django_rls_tenants.tenants.types.TenantUser
¶
Bases: Protocol
Protocol that user objects must satisfy for RLS context resolution.
Implement these two properties on your User model::
class User(AbstractUser, RLSProtectedModel):
@property
def is_tenant_admin(self) -> bool:
return self.role.name == "ADMIN"
@property
def rls_tenant_id(self) -> int | str | None:
return self.tenant_id if self.tenant_id else None
Bypass Helpers¶
django_rls_tenants.tenants.bypass.set_bypass_flag
¶
Set a bypass flag on the current database connection.
The flag name should match one of the extra_bypass_flags
configured on an RLSConstraint::
set_bypass_flag("rls.is_login_request")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
flag_name
|
str
|
GUC variable name (e.g., |
required |
is_local
|
bool
|
If |
False
|
using
|
str
|
Database alias. Default: |
'default'
|
Source code in django_rls_tenants/tenants/bypass.py
django_rls_tenants.tenants.bypass.clear_bypass_flag
¶
Clear a bypass flag on the current database connection.
Testing¶
django_rls_tenants.tenants.testing.rls_bypass
¶
Temporarily enable admin bypass for tests.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
using
|
str
|
Database alias. Default: |
'default'
|
Usage::
with rls_bypass():
all_orders = Order.objects.all() # sees all tenants
Source code in django_rls_tenants/tenants/testing.py
django_rls_tenants.tenants.testing.rls_as_tenant
¶
Scope to a specific tenant for tests.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tenant_id
|
int | str
|
The tenant PK to scope to. |
required |
using
|
str
|
Database alias. Default: |
'default'
|
Usage::
with rls_as_tenant(tenant_id=42):
orders = Order.objects.all() # only tenant 42
Source code in django_rls_tenants/tenants/testing.py
django_rls_tenants.tenants.testing.assert_rls_enabled
¶
Assert that RLS is enabled and forced on the given table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
The database table name to check. |
required |
using
|
str
|
Database alias. Default: |
'default'
|
Raises:
| Type | Description |
|---|---|
AssertionError
|
If RLS is not enabled or not forced. |
Source code in django_rls_tenants/tenants/testing.py
django_rls_tenants.tenants.testing.assert_rls_policy_exists
¶
assert_rls_policy_exists(
table_name: str,
policy_name: str | None = None,
*,
using: str = "default",
) -> None
Assert that an RLS policy exists on the given table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
table_name
|
str
|
The database table name to check. |
required |
policy_name
|
str | None
|
Expected policy name. Defaults to
|
None
|
using
|
str
|
Database alias. Default: |
'default'
|
Source code in django_rls_tenants/tenants/testing.py
django_rls_tenants.tenants.testing.assert_rls_blocks_without_context
¶
Assert that querying with no GUC context returns zero rows.
Verifies the fail-closed behavior. Requires at least one row to exist in the table (caller must set up test data first).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_class
|
type[Model]
|
The RLS-protected model class to query. |
required |
using
|
str
|
Database alias. Default: |
'default'
|
Raises:
| Type | Description |
|---|---|
AssertionError
|
If any rows are returned, or if the table is empty (which would make the assertion pass vacuously). |