Quick Start¶
This tutorial walks you through setting up django-rls-tenants from scratch. By the end you will have a working multitenant application with database-enforced row isolation.
1. Define Your Tenant Model¶
Create a model to represent tenants. Any model with an integer or UUID primary key works:
from django.db import models
class Tenant(models.Model):
name = models.CharField(max_length=255)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return self.name
2. Protect Models with RLS¶
Inherit from RLSProtectedModel to add automatic tenant isolation:
from django_rls_tenants import RLSProtectedModel
class Order(RLSProtectedModel):
title = models.CharField(max_length=255)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def __str__(self) -> str:
return self.title
RLSProtectedModel automatically:
- Adds a
tenantForeignKey pointing to your tenant model. - Sets
RLSManageras the default manager (withfor_user()support). - Includes an
RLSConstraintthat generates the RLS policy during migrations.
3. Implement the TenantUser Protocol¶
Your User model must expose two properties so the library knows which tenant a user belongs to and whether they are an admin:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser, RLSProtectedModel):
# Override the auto-generated tenant FK to allow null (for admins)
tenant = models.ForeignKey(
Tenant,
on_delete=models.CASCADE,
null=True,
blank=True,
)
@property
def is_tenant_admin(self) -> bool:
"""Admins bypass RLS and see all tenants."""
return self.is_superuser
@property
def rls_tenant_id(self) -> int | None:
"""Return the tenant PK for RLS filtering."""
return self.tenant_id if self.tenant_id else None
See User Integration for details on the TenantUser protocol.
4. Configure Settings¶
RLS_TENANTS = {
# Required: dotted path to your tenant model
"TENANT_MODEL": "myapp.Tenant",
# Optional (shown with defaults):
"TENANT_FK_FIELD": "tenant", # FK field name on protected models
"GUC_PREFIX": "rls", # PostgreSQL GUC variable prefix
"USER_PARAM_NAME": "as_user", # Parameter name for @with_rls_context
"TENANT_PK_TYPE": "int", # SQL cast type: "int", "bigint", or "uuid"
"USE_LOCAL_SET": False, # Use SET LOCAL (for connection pooling)
}
See Configuration for a detailed explanation of each setting.
5. Add Middleware¶
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
# Add after AuthenticationMiddleware:
"django_rls_tenants.RLSTenantMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
]
Important
RLSTenantMiddleware must come after AuthenticationMiddleware because it
reads request.user to determine the tenant context.
6. Run Migrations¶
The migration will:
- Create your tables as usual.
- Execute
ALTER TABLE ... ENABLE ROW LEVEL SECURITYandFORCE ROW LEVEL SECURITY. - Create a
CREATE POLICYstatement with tenant isolation rules.
7. Verify RLS Policies¶
Expected output:
Order (myapp_order): myapp_order_tenant_isolation_policy
User (myapp_user): myapp_user_tenant_isolation_policy
All 2 RLS-protected tables verified.
If any policies are missing, the command exits with a non-zero status and lists the issues.
What's Next?¶
- Configuration -- understand all 6 settings.
- Protecting Models -- customize FK fields and constraints.
- Context Managers -- use
tenant_contextandadmin_contextin scripts. - Testing -- test helpers for RLS verification.