{"id":5191,"library":"django-tenants","title":"django-tenants","description":"Django-tenants is a Python library that enables multi-tenancy for Django applications by leveraging PostgreSQL schemas. It allows a single Django project instance to serve multiple customers (tenants), each with isolated data, a crucial feature for Software-as-a-Service (SaaS) platforms. The library automates schema switching based on request hostnames, ensuring data isolation and efficient resource utilization. It is actively maintained, with the current stable version being 3.10.1, and receives regular updates to support new Django and Python versions. [1, 6, 7]","status":"active","version":"3.10.1","language":"en","source_language":"en","source_url":"https://github.com/django-tenants/django-tenants","tags":["Django","multi-tenancy","PostgreSQL","SaaS","schemas","database"],"install":[{"cmd":"pip install django-tenants","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core web framework dependency.","package":"Django"},{"reason":"Database backend required for schema-based multi-tenancy. [1]","package":"PostgreSQL"},{"reason":"PostgreSQL adapter for Python. `django-tenants` no longer lists it as a direct requirement but it's necessary for database connectivity. `psycopg3` is also supported. [13, 15]","package":"psycopg2-binary","optional":true},{"reason":"Modern PostgreSQL adapter for Python. `django-tenants` no longer lists it as a direct requirement but it's necessary for database connectivity. `psycopg2-binary` is also supported. [13, 15]","package":"psycopg3","optional":true}],"imports":[{"note":"TenantMixin is part of the django_tenants package, not typically a local 'tenants' app.","wrong":"from tenants.models import TenantMixin","symbol":"TenantMixin","correct":"from django_tenants.models import TenantMixin"},{"symbol":"DomainMixin","correct":"from django_tenants.models import DomainMixin"},{"symbol":"TenantMainMiddleware","correct":"from django_tenants.middleware.main import TenantMainMiddleware"},{"symbol":"TenantSyncRouter","correct":"from django_tenants.routers import TenantSyncRouter"},{"symbol":"schema_context","correct":"from django_tenants.utils import schema_context"},{"symbol":"tenant_context","correct":"from django_tenants.utils import tenant_context"},{"symbol":"TenantAdminMixin","correct":"from django_tenants.admin import TenantAdminMixin"}],"quickstart":{"code":"import os\nimport django\nfrom django.conf import settings\nfrom django.core.management import call_command\n\nos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project_name.settings')\ndjango.setup()\n\n# Assuming you have an app 'customers' with Client (TenantMixin) and Domain (DomainMixin) models\n# from customers.models import Client, Domain # Uncomment in your actual project\n\n# --- Example of creating a public tenant (adapt to your models) ---\n# In your settings.py:\n# TENANT_MODEL = \"customers.Client\"\n# TENANT_DOMAIN_MODEL = \"customers.Domain\"\n\n# Create a dummy Client and Domain for demonstration if not already existing\n# (In a real scenario, this would involve your actual Client and Domain models)\nclass MockClient(object):\n    id = 1 # Dummy ID\n    schema_name = 'public'\n    name = 'Public Tenant'\n\nclass MockDomain(object):\n    domain = 'localhost'\n    tenant = MockClient()\n    is_primary = True\n\n\nprint(\"1. Ensure settings are configured (SHARED_APPS, TENANT_APPS, MIDDLEWARE, DATABASE_ROUTERS).\")\nprint(\"2. Run initial migrations for shared schema:\")\ntry:\n    # In a real project, this would be `call_command('migrate_schemas', '--shared')`\n    # For this quickstart, we'll simulate output, as actual migration requires a full Django setup\n    print(\"   Simulating: python manage.py migrate_schemas --shared\")\n    # call_command('migrate_schemas', '--shared', verbosity=0)\n    print(\"   Shared schema migrations complete.\")\nexcept Exception as e:\n    print(f\"   Error during shared migrations (expected if not a full Django setup): {e}\")\n\nprint(\"3. Create a tenant (e.g., in a Django shell or a management command):\")\ntry:\n    # Example of creating a tenant (replace with your actual Client/Domain models and logic)\n    # tenant = Client(schema_name='tenant1', name='Tenant One', paid_until='2030-12-31', on_trial=False)\n    # tenant.save() # This automatically creates and syncs the schema\n\n    # domain = Domain()\n    # domain.domain = 'tenant1.localhost'\n    # domain.tenant = tenant\n    # domain.is_primary = True\n    # domain.save()\n\n    print(\"   Simulating tenant creation:\")\n    print(\"   client = Client(schema_name='tenant1', name='Tenant One', ...)\")\n    print(\"   client.save() # Schema 'tenant1' created and migrated automatically\")\n    print(\"   domain = Domain(domain='tenant1.localhost', tenant=client, is_primary=True)\")\n    print(\"   domain.save()\")\n    print(\"   Tenant 'tenant1' created with domain 'tenant1.localhost'.\")\nexcept Exception as e:\n    print(f\"   Error during tenant creation (expected if not a full Django setup): {e}\")\n\nprint(\"4. Access tenant-specific data via hostname (e.g., tenant1.localhost:8000).\")\nprint(\"   The TenantMainMiddleware will automatically switch the database schema.\")\n","lang":"python","description":"This quickstart demonstrates the core steps for setting up `django-tenants`. It involves configuring your Django `settings.py` with `SHARED_APPS`, `TENANT_APPS`, `DATABASE_ROUTERS`, and `TenantMainMiddleware`. You then define your `TenantMixin` and `DomainMixin` models, run initial migrations for shared applications using `migrate_schemas --shared`, and finally create tenant instances, which automatically create and migrate their respective schemas. [5, 8, 12, 16]"},"warnings":[{"fix":"Review `django-tenants` release notes (e.g., GitHub releases) before upgrading to ensure compatibility with your Django and Python environment. Upgrade your Django and Python versions if necessary.","message":"`django-tenants` v3.x has aligned with newer Django and Python versions. Specifically, v3.10.0 and v3.8.0 added support for Django 5.x and Python 3.13, while dropping support for older Django versions (e.g., 3.x, 4.0) and Python versions (e.g., 3.8, 3.1) in previous v3.x releases. Always check release notes for specific version compatibility when upgrading. [15]","severity":"breaking","affected_versions":"3.x onwards"},{"fix":"Explicitly install your preferred PostgreSQL adapter: `pip install psycopg2-binary` or `pip install \"psycopg[binary]\"` (for psycopg3).","message":"As of v3.0.0, `django-tenants` removed `psycopg2` as a direct dependency. While this provides flexibility, it means users must explicitly install a PostgreSQL adapter like `psycopg2-binary` or `psycopg3` for database connectivity. [13]","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Exercise extreme caution when setting `auto_drop_schema = True` on your tenant model. Understand the implications and ensure proper backup strategies or manual schema management if this flag is enabled.","message":"The `auto_drop_schema` field on your `TenantMixin` model defaults to `False`. If you explicitly set it to `True`, deleting a tenant model instance through the ORM will *automatically drop its associated PostgreSQL schema* without further confirmation. This can lead to irreversible data loss. [3]","severity":"gotcha","affected_versions":"All versions"},{"fix":"Always use `migrate_schemas --shared` for shared applications and `tenant_command migrate` or `all_tenants_command` for tenant-specific applications. Do not use `manage.py migrate` directly after initial setup.","message":"Running the standard `python manage.py migrate` command will apply migrations to *both* shared and tenant schemas, which is often not the desired behavior. You must use `python manage.py migrate_schemas --shared` for shared apps and `python manage.py tenant_command migrate --schema=yourtenant` (or `all_tenants_command`) for tenant-specific apps. [5]","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure your `TEMPLATES` setting includes `'django.template.context_processors.request'` in the `'OPTIONS': {'context_processors': [...]}` list.","message":"For `request.tenant` to be available in your templates, `django.template.context_processors.request` must be included in the `context_processors` option within your `TEMPLATES` setting in `settings.py`. [5]","severity":"gotcha","affected_versions":"All versions"},{"fix":"For ASGI applications, ensure tenant context is explicitly passed or managed within the async request/task scope. Prefer `schema_context` or `tenant_context` utilities for explicit context switching in non-request-bound operations.","message":"When working with ASGI applications (e.g., Dpahne, Uvicorn) and custom tenant resolution logic outside of `TenantMainMiddleware`, relying on `thread_locals` or similar global state for tenant context can be problematic due to the asynchronous nature of ASGI servers. This can lead to incorrect tenant context being applied. [17]","severity":"gotcha","affected_versions":"All versions (especially with ASGI)"}],"env_vars":null,"last_verified":"2026-04-13T00:00:00.000Z","next_check":"2026-07-12T00:00:00.000Z"}