Skip to content

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:

myapp/models.py
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:

myapp/models.py
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 tenant ForeignKey pointing to your tenant model.
  • Sets RLSManager as the default manager (with for_user() support).
  • Includes an RLSConstraint that 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:

myapp/models.py
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

settings.py
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

settings.py
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

python manage.py makemigrations
python manage.py migrate

The migration will:

  1. Create your tables as usual.
  2. Execute ALTER TABLE ... ENABLE ROW LEVEL SECURITY and FORCE ROW LEVEL SECURITY.
  3. Create a CREATE POLICY statement with tenant isolation rules.

7. Verify RLS Policies

python manage.py check_rls

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?