Skip to main content
https://badge.fury.io/py/django-tenants.svg Build status https://readthedocs.org/projects/pip/badge/?version=latest https://img.shields.io/pypi/dm/django-tenants.svg?maxAge=180 https://codecov.io/gh/django-tenants/django-tenants/branch/master/graph/badge.svg?token=wCNgSgTTR8

This application enables django powered websites to have multiple tenants via PostgreSQL schemas. A vital feature for every Software-as-a-Service (SaaS) website.

Read the full documentation here: django-tenants.readthedocs.org

Django provides currently no simple way to support multiple tenants using the same project instance, even when only the data is different. Because we don’t want you running many copies of your project, you’ll be able to have:

  • Multiple customers running on the same instance

  • Shared and Tenant-Specific data

  • Tenant View-Routing

What are schemas

A schema can be seen as a directory in an operating system, each directory (schema) with its own set of files (tables and objects). This allows the same table name and objects to be used in different schemas without conflict. For an accurate description on schemas, see PostgreSQL’s official documentation on schemas.

Why schemas

There are typically three solutions for solving the multitenancy problem.

  1. Isolated Approach: Separate Databases. Each tenant has its own database.

  2. Semi Isolated Approach: Shared Database, Separate Schemas. One database for all tenants, but one schema per tenant.

  3. Shared Approach: Shared Database, Shared Schema. All tenants share the same database and schema. There is a main tenant-table, where all other tables have a foreign key pointing to.

This application implements the second approach, which in our opinion, represents the ideal compromise between simplicity and performance.

  • Simplicity: barely make any changes to your current code to support multitenancy. Plus, you only manage one database.

  • Performance: make use of shared connections, buffers and memory.

Each solution has its up and down sides. For a more in-depth discussion, see Microsoft’s excellent article on Multi-Tenant Data Architecture.

How it works

Tenants are identified via their host name (i.e tenant.domain.com). This information is stored on a table on the public schema. Whenever a request is made, the host name is used to match a tenant in the database. If there’s a match, the search path is updated to use this tenant’s schema. So from now on all queries will take place at the tenant’s schema. For example, suppose you have a tenant customer at http://customer.example.com. Any request incoming at customer.example.com will automatically use customer’s schema and make the tenant available at the request. If no tenant is found, a 404 error is raised. This also means you should have a tenant for your main domain, typically using the public schema. For more information please read the installation section.

What can this app do?

As many tenants as you want

Each tenant has its data on a specific schema. Use a single project instance to serve as many as you want.

Tenant-specific and shared apps

Tenant-specific apps do not share their data between tenants, but you can also have shared apps where the information is always available and shared between all.

Tenant View-Routing

You can have different views for http://customer.example.com/ and http://example.com/, even though Django only uses the string after the host name to identify which view to serve.

Magic

Everyone loves magic! You’ll be able to have all this barely having to change your code!

Setup & Documentation

This is just a short setup guide. It is strongly recommended that you read the complete version at django-tenants.readthedocs.org.

Your DATABASE_ENGINE setting needs to be changed to

DATABASES = {
    'default': {
        'ENGINE': 'django_tenants.postgresql_backend',
        # ..
    }
}

Add the middleware django_tenants.middleware.main.TenantMainMiddleware to the top of MIDDLEWARE, so that each request can be set to use the correct schema.

MIDDLEWARE = (
    'django_tenants.middleware.main.TenantMainMiddleware',
    #...
)

Add django_tenants.routers.TenantSyncRouter to your DATABASE_ROUTERS setting, so that the correct apps can be synced depending on what’s being synced (shared or tenant).

DATABASE_ROUTERS = (
    'django_tenants.routers.TenantSyncRouter',
)

Add django_tenants to your INSTALLED_APPS.

Create your tenant model

from django.db import models
from django_tenants.models import TenantMixin, DomainMixin

class Client(TenantMixin):
    name = models.CharField(max_length=100)
    paid_until = models.DateField()
    on_trial = models.BooleanField()
    created_on = models.DateField(auto_now_add=True)

class Domain(DomainMixin):
    pass

Define on settings.py which model is your tenant model. Assuming you created Client inside an app named customers, your TENANT_MODEL should look like this:

TENANT_MODEL = "customers.Client" # app.Model
TENANT_DOMAIN_MODEL = "customers.Domain" # app.Model

Now run migrate_schemas. This will sync your apps to the public schema.

python manage.py migrate_schemas --shared

Create your tenants just like a normal django model. Calling save will automatically create and sync the schema.

from customers.models import Client, Domain

# create your public tenant
tenant = Client(schema_name='tenant1',
                name='My First Tenant',
                paid_until='2014-12-05',
                on_trial=True)
tenant.save()

# Add one or more domains for the tenant
domain = Domain()
domain.domain = 'tenant.my-domain.com'
domain.tenant = tenant
domain.is_primary = True
domain.save()

Any request made to tenant.my-domain.com will now automatically set your PostgreSQL’s search_path to tenant1 and public, making shared apps available too. This means that any call to the methods filter, get, save, delete or any other function involving a database connection will now be done at the tenant’s schema, so you shouldn’t need to change anything at your views.

You’re all set, but we have left key details outside of this short tutorial, such as creating the public tenant and configuring shared and tenant specific apps. Complete instructions can be found at django-tenants.readthedocs.org.

Running the example project

django-tenants comes with an example project please see

examples.

Credits

I would like to thank two of the original authors of this project.

  1. Bernardo Pires under the name django-tenant-schemas.

  2. Vlada Macek under the name of django-schemata.

Requirements

  • Django 2 if you want to use Django 1.11 or lower please use version 1 of django-tenants

  • PostgreSQL

Testing

If you want to run tests, you can either run run_tests.sh (which requires access to a PostgreSQL instance, location of which you can customize using the DATABASE_HOST env variable) or use docker-compose like this:

## Start Docker service
# start docker   # with Upstart
# systemctl start docker  # with systemd

## Install docker-compose (you might want to do this in Python virtualenv)
# pip install docker-compose

## In main directory of this repo do:
docker-compose run --rm django-tenants-test  # runs django-tenants tests.
# dockerized PostgreSQL service is started implicitly

(note that upon first run the Dockerfile will be built).

Video Tutorial

An online video tutorial is available on youtube.

Donation

If this project helped you reduce development time, you can give me cake :)

https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif

Release files for django-tenants 3.14.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-tenants 3.14.0
File Size Uploaded
django_tenants-3.14.0.tar.gz 168.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-tenants 3.14.0
File Interpreter ABI Platform
django_tenants-3.14.0-py3-none-any.whl Python 3 none any Details

Total release size: 295.2 kB

Release files / django_tenants-3.14.0.tar.gz

Download URL django_tenants-3.14.0.tar.gz
Size 168.1 kB
Tags Source
SHA-256 checksum
How to use checksums
55dc64bcd4293a03fab7108f4c4e1724c2f53ef09a1c7f9e5f75a136fccc3146
BLAKE2b-256 checksum
How to use checksums
a344f78ed75c27e019be3ba915d88484bf57355cc7dd92745ff27d23431fe61c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.

Transparency log

Release files / django_tenants-3.14.0-py3-none-any.whl

Download URL django_tenants-3.14.0-py3-none-any.whl
Size 127.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f2c17b05d03c8b8d79c8e190ca2da14fdc36d52553d216b1dbb09c4adbda2eb6
BLAKE2b-256 checksum
How to use checksums
7ca2c4d45511b0c1c3868fafa9e745b6089c0e0b30959b8191ac4a5643bac9ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

3.14.0 This release

2 release files

3.10.2

2 release files

3.10.1

2 release files

3.9.0

2 release files

3.8.0

1 release file

3.7.0

1 release file

3.6.1

1 release file

3.6.0

1 release file

3.5.0

1 release file

3.4.8

1 release file

3.4.7

1 release file

3.4.6

1 release file

3.4.5

1 release file

3.4.4

1 release file

3.4.3

1 release file

3.4.2

1 release file

3.4.1

1 release file

3.4.0

1 release file

3.3.4

1 release file

3.3.2

1 release file

3.3.1

1 release file

3.3.0

1 release file

3.2.1

1 release file

3.2.0

1 release file

3.1.0

1 release file

3.0.3

1 release file

3.0.2

1 release file

3.0.1

1 release file

3.0.0

1 release file

2.2.3

1 release file

2.2.2

1 release file

2.2.1

1 release file

2.2.0

1 release file

2.1.0

1 release file

2.0.0

1 release file

1.3.4

1 release file

1.3.3

1 release file

1.3.2

1 release file

1.3.1

1 release file

1.3.0

1 release file

1.2.1

1 release file

1.2.0

1 release file

1.1.9

1 release file

1.1.8

1 release file

1.1.7

1 release file

1.1.6

1 release file

1.1.5

1 release file

1.1.4

1 release file

1.1.3

1 release file

1.1.2

1 release file

1.1.1

1 release file

1.1.0

1 release file

1.0.3

1 release file

1.0.2

1 release file

1.0.1

1 release file

1.0.0

1 release file

0.9.1

1 release file

0.9

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page