// the find
bernardopires/django-tenant-schemas
Tenant support for Django using PostgreSQL schemas.
Adds PostgreSQL-schema-based multitenancy to Django: each tenant gets its own schema, requests are routed to the right one by matching hostname against a tenant model, and the search_path gets swapped per-request. It's for SaaS teams who want the simplicity of a single app instance and single database connection pool, without going full shared-table multitenancy.
Picks a genuinely middle-ground architecture — tenant isolation via native PostgreSQL schemas instead of a bolted-on tenant_id column on every table, so most existing queries just work unmodified once the search_path is set. Ships a parallel migration executor (migration_executors/parallel.py) for running migrate_schemas across hundreds of tenant schemas concurrently, which is the first thing that becomes a bottleneck at scale. Supports both psycopg2 and psycopg3 as installable extras, and there's a dedicated test file for a psycopg3 recursion bug (test_psycopg3_recursion.py), suggesting the maintainer actually hit and fixed that edge case rather than just claiming compatibility.
The whole mechanism rests on mutating the database connection's search_path mid-request via middleware — the README calls this 'magic,' and that's also exactly the failure mode: any connection reuse across requests that isn't funneled through this middleware (background tasks, management commands, transaction-pooling setups like pgbouncer in transaction mode) can silently run queries against the wrong tenant's data. No mention anywhere of how Celery tasks or async views are supposed to pick up the correct schema context, which is the most common way teams get burned with this pattern in production. The project is also one of several forks of the original tomturner/django-tenant-schemas lineage (this is bernardopires's fork), and competing alternatives like django-tenants and django-pgschemas cover similar ground — worth checking which fork is actually getting fixes before committing to one. Tenant resolution is hostname-only out of the box, so path-based or header-based tenant routing needs custom middleware work.