Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
1.0.0 - 2026-03-15¶
Initial stable release of django-rls-tenants.
Added¶
Architecture¶
- Two-layer architecture with strict import boundary enforced by tests:
rls/layer: generic PostgreSQL RLS primitives with zero Django model knowledge.tenants/layer: opinionated Django multitenancy built on therls/layer.
RLS Primitives (rls/)¶
set_guc,get_guc,clear_guchelpers for managing PostgreSQL session variables (GUCs), with regex-based SQL injection prevention.RLSConstraintfor generatingCREATE POLICY/DROP POLICYDDL in Django migrations, with support forint,bigint, anduuidprimary key types.rls_contextgeneric context manager for setting/clearing arbitrary GUC variables with save/restore nesting support.bypass_flagcontext manager for toggling boolean bypass flags within a transaction-scoped context.
Tenant Models & Managers (tenants/)¶
RLSProtectedModelabstract base class with dynamic tenant foreign key added via theclass_preparedsignal. Supports auto-generated and explicit FK configurations.TenantQuerySetwith lazy GUC evaluation at query execution time (not queryset creation time), solving the lazy evaluation problem for chained queries.RLSManagerwithfor_user()for scoped queries andprepare_tenant_in_model_data()for efficient bulk creation without N+1SELECTqueries.- Defense-in-depth:
for_user()applies both Django ORM.filter()and database-level RLS, so even if one layer is bypassed the other provides isolation.
Middleware & Context¶
RLSTenantMiddlewarefor automatic per-request RLS context based on the authenticated user. API-agnostic (works with REST, GraphQL, Django views).tenant_contextandadmin_contextcontext managers with nesting support and automatic GUC cleanup viatry/finally.@with_rls_contextdecorator for extracting user context from function arguments, with configurableuser_paramand fail-closed behavior.request_finishedsignal safety net for GUC cleanup in case middleware'sprocess_responsedoes not run.
Configuration & Validation¶
- Single
RLS_TENANTSsettings dict with 6 configuration keys:TENANT_MODEL,TENANT_FK_FIELD,USER_PARAM_NAME,GUC_PREFIX,USE_LOCAL_SET,CONN_MAX_AGE_OVERRIDE. RLSTenantsConfigsingleton with lazy property access and unknown-key detection (warns on typos).- Django system checks:
W001(GUC prefix mismatch for tenant),W002(GUC prefix mismatch for admin),W003(USE_LOCAL_SETwithoutATOMIC_REQUESTS),W004(CONN_MAX_AGE > 0with session-scoped GUCs).
User Integration¶
TenantUserruntime-checkableProtocolfor structural subtyping of user objects. Requiresis_tenant_admin,rls_tenant_id, andis_authenticated.
Bypass Mode¶
bypass_flagcontext manager for temporary bypass of specific RLS policies.set_bypass_flag/clear_bypass_flagimperative helpers.extra_bypass_flagssupport onRLSConstraintfor custom bypass clauses (e.g., login flows).
Connection Pooling¶
USE_LOCAL_SETconfiguration for transaction-scoped GUCs viaSET LOCAL, compatible with PgBouncer and other connection poolers.
Management Commands¶
check_rlsmanagement command to verify RLS policies are correctly applied to all protected models, with CI-friendly exit codes.
Testing Utilities¶
rls_bypasscontext manager for disabling RLS in test setup/teardown.rls_as_tenantcontext manager for running tests as a specific tenant.assert_rls_enabledassertion for verifying RLS is active on a table.assert_rls_policy_existsassertion for verifying a named policy exists.assert_rls_blocks_without_contextassertion for verifying fail-closed behavior.
Package & Tooling¶
- PEP 561
py.typedmarker for typed package support. __version__attribute viaimportlib.metadata(single source of truth).- Full MkDocs Material documentation site with 19 pages: getting started, guides, advanced topics, API reference, and development docs.
- CI pipeline: Python {3.11, 3.12, 3.13, 3.14} x Django {4.2, 5.0, 5.1, 5.2, 6.0} test matrix with PostgreSQL 16.
- OIDC trusted publishing to PyPI via GitHub Actions.
- Security policy (
SECURITY.md) with vulnerability disclosure process.
Fixed¶
clear_gucnow accepts anis_localparameter, ensuring consistent GUC lifetimes whenUSE_LOCAL_SET=True. Previously,admin_context, middleware, and manager_fetch_allcould clear GUCs with session scope while setting them with transaction scope, causing mismatched lifetimes.