Database-enforced multitenancy for Django using PostgreSQL
Row-Level Security.
Every query -- ORM, raw SQL, dbshell -- is filtered by the database itself.
Missing tenant context returns zero rows, never leaks data.
Existing Django multitenancy libraries enforce isolation at the application
level — one missed filter and data leaks across tenants. django-rls-tenants
pushes enforcement to PostgreSQL itself using Row-Level Security. Every query —
ORM, raw SQL, dbshell — is filtered by the database. Missing tenant context
returns zero rows, never leaked data.
Learn more about the RLS approach →
pip install django-rls-tenants# settings.py
INSTALLED_APPS = [..."django_rls_tenants"]
RLS_TENANTS = {
"TENANT_MODEL": "myapp.Tenant",
"TENANT_FK_FIELD": "tenant",
"GUC_PREFIX": "rls",
"USER_PARAM_NAME": "as_user",
"TENANT_PK_TYPE": "int",
"USE_LOCAL_SET": False,
"DATABASES": ["default"],
"STRICT_MODE": False,
}
MIDDLEWARE = [..."django_rls_tenants.RLSTenantMiddleware"]# models.py
from django_rls_tenants import RLSProtectedModel
class Order(RLSProtectedModel):
title = models.CharField(max_length=255)
# tenant FK + RLS policy added automaticallypython manage.py migrate
python manage.py check_rls # verify policies are in placeFull tutorial and documentation →
Want to see it in action first? Check out the example project --
cd example && docker compose up, then open http://localhost:8000.
| Feature | django-rls-tenants | django-tenants | django-multitenant |
|---|---|---|---|
| Isolation mechanism | RLS policies | Separate schemas | ORM rewriting |
| Raw SQL protected | Yes | Yes (schemas) | No |
| Single schema | Yes | No | Yes |
| No connection routing | Yes | No | Depends |
| Fail-closed on missing ctx | Yes | N/A | No |
| Works with any API layer | Yes | Yes | Yes |
Are you using django-rls-tenants in production? We'd love to hear about it.
Open a discussion →
| Dependency | Version |
|---|---|
| Python | >= 3.11 |
| Django | >= 4.2 |
| PostgreSQL | >= 15 |