django-tenants

  • Running commands in tenant
    ./manage.py tenant_command constance list -s grande
  • Tenant setup
    # Tenant Creation Example
    ```sh
      python manage.py create_tenant \
          --domain-domain=api.client1.x.io \
          --schema_name=client1 \
          --name="Client 1"
    ```
    ## This creates:
      - Tenant record: schema_name="client1", name="Client 1"
      - Domain record: domain="api.client1.x.io" → tenant_id=$tenant_id
      - PostgreSQL schema: Creates client1 schema with all tenant-specific tables
    
    ## Request Example
    
    When a request comes to https://api.client1.x.io/api/v1/audits/:
    
      1. Middleware extracts hostname: api.client1.x.io
      2. Queries: SELECT * FROM tenancy_domain WHERE domain='api.client1.x.io'
      3. Gets tenant with schema_name='client1'
      4. Switches PostgreSQL connection to client1 schema
      5. All subsequent queries operate within client1 schema
    
    
    
    
    # Using localhost on laptop
    
    ## Development Setup Options
    
      1. Localhost with Port (Simplest)
    ```sh
      python manage.py create_tenant \
          --domain-domain=localhost:8000 \
          --schema_name=dev1 \
          --name="Dev Tenant 1"
    ```
      Access via: http://localhost:8000
    
      2. Subdomain Localhost (Recommended)
    ```sh
      python manage.py create_tenant \
          --domain-domain=client1.localhost \
          --schema_name=client1 \
          --name="Client 1 Dev"
    
      python manage.py create_tenant \
          --domain-domain=client2.localhost \
          --schema_name=client2 \
          --name="Client 2 Dev"
    ```
      Add to /etc/hosts (on Mac/Linux) or C:\Windows\System32\drivers\etc\hosts (Windows):
    ```sh
      127.0.0.1 client1.localhost
      127.0.0.1 client2.localhost
    ```
    
      Access via:
      - http://client1.localhost:8000
      - http://client2.localhost:8000
    
      3. Docker Development
    
    ```sh
      docker compose -f local.yml run --rm django python manage.py create_tenant \
          --domain-domain=xxx.localhost \
          --schema_name=xxx \
          --name=xxx
    ```
    
      4. Public Schema Fallback
    
      The system is configured with:
    ```sh
      SHOW_PUBLIC_IF_NO_TENANT_FOUND = True  # config/settings/base.py:196
    ```
    
      This means if no tenant matches the domain, it falls back to the public schema instead of
      throwing a 404.
    
      How It Works in Development
    
      The middleware (TenantMainMiddleware) extracts the hostname using:
    
    ```python
      hostname = remove_www(request.get_host().split(':')[0])
    ```
    
      So for http://client1.localhost:8000, it extracts client1.localhost and matches it against the
       database.
    
      Testing Multiple Tenants
    
      You can easily test multiple tenants locally:
      # Create multiple tenants
    ```sh
      python manage.py create_tenant --domain-domain=tenant1.localhost --schema_name=tenant1
      --name="Tenant 1"
      python manage.py create_tenant --domain-domain=tenant2.localhost --schema_name=tenant2
      --name="Tenant 2"
    
      # Access different tenants
      curl http://tenant1.localhost:8000/api/v1/audits/
      curl http://tenant2.localhost:8000/api/v1/audits/
    
    ```
      Each will access completely separate data in their own PostgreSQL schemas (tenant1 and
      tenant2).
    
      No real DNS required - just local hostname resolution via /etc/hosts or browser requests.