Skip to main content

A Django app to allow multi-tenancy.

Project description

django-omnitenant

Python Django License Stars

Multi-tenancy made simple for Django applications!


๐Ÿš€ Overview

django-omnitenant is a powerful, flexible multi-tenancy solution for Django that allows you to efficiently manage multiple isolated tenants within a single Django application. Whether you're building a SaaS platform, a shared hosting environment, or a complex multi-tenant application, django-omnitenant provides the tools you need to handle tenant isolation at the database, schema, or hybrid level.

Key Features

  • โœ” Flexible Tenant Isolation โ€“ Choose between database-per-tenant, schema-per-tenant, or hybrid isolation strategies(Coming soon...)
  • โœ” Automatic Tenant Context โ€“ Seamlessly switch between tenants with context managers
  • โœ” Admin Restrictions โ€“ Restrict admin access to tenant-specific models
  • โœ” Tenant-Aware Middleware โ€“ Automatically resolve tenants from HTTP requests
  • โœ” Tenant-Aware Caching โ€“ Cache keys prefixed with tenant IDs
  • โœ” Tenant-Aware Celery โ€“ Run Celery tasks in the context of a specific tenant
  • โœ” Comprehensive CLI Tools โ€“ Create, manage, and migrate tenants with ease
  • โœ” Signal System โ€“ Hook into tenant lifecycle events (creation, deletion, migration)
  • โœ” Customizable Tenant Resolution โ€“ Resolve tenants from subdomains, custom domains, or other sources

โœจ Features

Multi-Tenancy Strategies

  • Database-per-Tenant: Each tenant has its own database
  • Schema-per-Tenant: All tenants share a single database but have separate schemas
  • Hybrid(soon...): Combine database and schema isolation for complex scenarios

Tenant Management

  • Create, delete, and manage tenants with a simple CLI
  • Run migrations for individual tenants or all tenants at once
  • Create superusers within specific tenants

Tenant-Aware Components

  • Middleware: Automatically resolves tenants from HTTP requests
  • Database Router: Routes queries to the correct database/schema
  • Cache Backend: Prefixes cache keys with tenant IDs
  • Celery Integration: Run tasks in the context of a specific tenant

Security & Permissions

  • Restrict admin access to tenant-specific models
  • Ensure tenant isolation at the database and schema level
  • Validate tenant IDs and domain names

Extensible Architecture

  • Custom tenant models and domain models
  • Custom tenant resolvers for flexible tenant resolution logic
  • Patch system for extending Django's core functionality

๐Ÿ› ๏ธ Tech Stack

  • Language: Python 3.8+
  • Framework: Django 3.2+
  • Database: PostgreSQL (recommended), MySQL(only for db type isolation)
  • Dependencies: Django, psycopg2 (for PostgreSQL), Celery (optional)
  • License: MIT

๐Ÿ“ฆ Installation

Prerequisites

Before installing django-omnitenant, ensure you have the following:

  • Python 3.8 or higher
  • Django 3.2 or higher
  • PostgreSQL (recommended) or another supported database
  • Basic familiarity with Django and Python

Quick Start

  1. Install django-omnitenant:
pip install django-omnitenant
  1. Add to your INSTALLED_APPS:
# settings.py
INSTALLED_APPS = [
    ...
    'django_omnitenant',
    ...
]
  1. Configure django-omnitenant:

Add the following to your settings.py:

# settings.py
OMNITENANT_CONFIG = {
    'TENANT_MODEL': 'myapp.Tenant',  # Your custom tenant model
    'DOMAIN_MODEL': 'myapp.Domain',  # Your custom domain model
    'PUBLIC_HOST': 'localhost',      # Default public host
    'PUBLIC_TENANT_NAME': 'public_omnitenant',  # Default public tenant
    'MASTER_TENANT_NAME': 'Master',  # Master tenant name
    'PATCHES': [
    "your custom patches's full path"
    ],
}
  1. Run migrations:
python manage.py migrate
  1. Add middleware:
# settings.py
MIDDLEWARE = [
    ...
    'django_omnitenant.middleware.TenantMiddleware',
    ...
]
  1. Configure your database router:
# settings.py
DATABASE_ROUTERS = [
    'django_omnitenant.routers.TenantRouter',
]
  1. Create your first tenant:
python manage.py createtenantsuperuser --tenant-id=mytenant

๐ŸŽฏ Usage

Basic Usage

Creating a Tenant

python manage.py createtenant

Running Migrations for a Tenant

python manage.py migratetenant --tenant-id=mytenant

Running Migrations for All Tenants

python manage.py migratealltenants

Shell with Tenant Context

python manage.py shell --tenant-id=mytenant

Show All Tenants

python manage.py showtenants

Advanced Usage: Custom Tenant Model

  1. Define your tenant model:
# models.py
from django.db import models
from django_omnitenant.models import BaseTenant

class Tenant(BaseTenant):
    description = models.TextField(blank=True, null=True)
  1. Configure django-omnitenant:
# settings.py
OMNITENANT_CONFIG = {
    'TENANT_MODEL': 'myapp.Tenant',
    ...
}

Advanced Usage: Custom Tenant Resolver

  1. Create a custom resolver:
# resolvers/custom_resolver.py
from django_omnitenant.resolvers.base import BaseTenantResolver
from django_omnitenant.utils import get_tenant_model

class CustomTenantResolver(BaseTenantResolver):
    def resolve(self, request):
        # Implement your custom logic to resolve the tenant
        tenant_id = request.GET.get('tenant_id')
        Tenant = get_tenant_model()
        return Tenant.objects.get(tenant_id=tenant_id)
  1. Configure the resolver:
# settings.py
OMNITENANT_CONFIG = {
    'TENANT_RESOLVER': 'myapp.resolvers.CustomTenantResolver',
    ...
}

Advanced Usage: Tenant-Aware Admin

# admin.py
from django.contrib import admin
from django_omnitenant.admin import _TenantRestrictAdminMixin

class MyModelAdmin(_TenantRestrictAdminMixin, admin.ModelAdmin):
    pass

admin.site.register(MyModel, MyModelAdmin)

๐Ÿ“ Project Structure

django-omnitenant/
โ”œโ”€โ”€ __init__.py
โ”œโ”€โ”€ admin.py
โ”œโ”€โ”€ apps.py
โ”œโ”€โ”€ bootstrap.py
โ”œโ”€โ”€ conf.py
โ”œโ”€โ”€ constants.py
โ”œโ”€โ”€ exceptions.py
โ”œโ”€โ”€ middleware.py
โ”œโ”€โ”€ models.py
โ”œโ”€โ”€ routers.py
โ”œโ”€โ”€ signals.py
โ”œโ”€โ”€ tenant_context.py
โ”œโ”€โ”€ utils.py
โ”œโ”€โ”€ validators.py
โ”œโ”€โ”€ views.py
โ”œโ”€โ”€ backends/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ base.py
โ”‚   โ”œโ”€โ”€ cache_backend.py
โ”‚   โ”œโ”€โ”€ database_backend.py
โ”‚   โ”œโ”€โ”€ postgresql/
โ”‚   โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”‚   โ””โ”€โ”€ base.py
โ”‚   โ””โ”€โ”€ schema_backend.py
โ”œโ”€โ”€ management/
โ”‚   โ””โ”€โ”€ commands/
โ”‚       โ”œโ”€โ”€ createtenant.py
โ”‚       โ”œโ”€โ”€ createsuperuser.py
โ”‚       โ”œโ”€โ”€ migratealltenants.py
โ”‚       โ”œโ”€โ”€ migratetenant.py
โ”‚       โ”œโ”€โ”€ shell.py
โ”‚       โ””โ”€โ”€ showtenants.py
โ”œโ”€โ”€ patches/
โ”‚   โ”œโ”€โ”€ cache.py
โ”‚   โ””โ”€โ”€ celery.py
โ”œโ”€โ”€ resolvers/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ base.py
โ”‚   โ”œโ”€โ”€ customdomain_resolver.py
โ”‚   โ””โ”€โ”€ subdomain_resolver.py
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ testcases.py
โ””โ”€โ”€ migrations/

๐Ÿ”ง Configuration

Environment Variables

No environment variables are required, but you can customize django-omnitenant by setting the following in your settings.py:

# Database and Cache Configuration
MASTER_DB_ALIAS = 'default'
PUBLIC_DB_ALIAS = 'public'
MASTER_CACHE_ALIAS = 'default'

# Tenant Configuration
MASTER_TENANT_NAME = 'Master'
PUBLIC_TENANT_NAME = 'public_omnitenant'
DEFAULT_SCHEMA_NAME = 'public'

# Tenant Model Configuration
OMNITENANT_CONFIG = {
    'TENANT_MODEL': 'myapp.Tenant',
    'DOMAIN_MODEL': 'myapp.Domain',
    'PUBLIC_HOST': 'localhost',
    'PUBLIC_TENANT_NAME': 'public_omnitenant',
    'MASTER_TENANT_NAME': 'Master',
    'PATCHES': [
        'django_omnitenant.patches.cache',
        'django_omnitenant.patches.celery',
    ],
}

Customizing Tenant Models

To customize your tenant model, create a model that inherits from BaseTenant:

# models.py
from django.db import models
from django_omnitenant.models import BaseTenant

class Tenant(BaseTenant):
    description = models.TextField(blank=True, null=True)

Customizing Domain Models

To customize your domain model, create a model that represents domains and link it to tenants:

# models.py
from django.db import models
from django_omnitenant.models import BaseTenant

class Domain(models.Model):
    domain = models.CharField(max_length=255, unique=True)
    tenant = models.ForeignKey(Tenant, on_delete=models.CASCADE)

    def __str__(self):
        return self.domain

๐Ÿค Contributing

We welcome contributions from the community! Here's how you can contribute to django-omnitenant:

Development Setup

  1. Clone the repository:
git clone https://github.com/yourusername/django-omnitenant.git
cd django-omnitenant
  1. Create a virtual environment:
python -m venv venv
source venv/bin/activate  # On Windows use `venv\Scripts\activate`
  1. Install dependencies:
pip install -e .
  1. Run tests:
python -m pytest tests/

Code Style Guidelines

  • Follow PEP 8 style guidelines
  • Use type hints where possible
  • Write clear, concise, and well-documented code
  • Ensure tests cover all functionality

Pull Request Process

  1. Fork the repository and create your branch from master.
  2. Make your changes and ensure they pass all tests.
  3. Submit a pull request with a clear description of your changes.

๐Ÿ“ License

This project is licensed under the MIT License - see the LICENSE file for details.


๐Ÿ‘ฅ Authors & Contributors

Maintainers:

Contributors:

  • Contributors are welcomed

๐Ÿ› Issues & Support

Reporting Issues

If you encounter any issues or have feature requests, please open an issue on the GitHub Issues page.

Getting Help

  • Documentation: Check out the wiki for detailed guides.
  • FAQ: Common questions and answers can be found in the FAQ section.

๐Ÿ—บ๏ธ Roadmap

Planned Features

  • Hybrid Isolation: Combine database and schema isolation for more complex scenarios
  • Tenant Analytics: Built-in tools for monitoring and analyzing tenant usage
  • Tenant Provisioning API: RESTful API for programmatically creating and managing tenants
  • Improved Documentation: More detailed guides and tutorials

Known Issues

  • PostgreSQL 16 Compatibility: Testing and ensuring compatibility with PostgreSQL 15
  • Celery Task Persistence: Improving task persistence across tenant switches

Future Improvements

  • Better Performance: Optimize database and cache operations for better performance
  • Enhanced Security: Additional security features and validations
  • More Resolvers: Additional tenant resolution strategies

๐ŸŒŸ Star and Share

If you find django-omnitenant useful, please consider giving it a star on GitHub! Sharing it with your network helps us reach more developers and continue improving the project.

Thank you for using django-omnitenant! ๐Ÿš€

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_omnitenant-0.2.2.tar.gz (28.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_omnitenant-0.2.2-py3-none-any.whl (35.3 kB view details)

Uploaded Python 3

File details

Details for the file django_omnitenant-0.2.2.tar.gz.

File metadata

  • Download URL: django_omnitenant-0.2.2.tar.gz
  • Upload date:
  • Size: 28.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for django_omnitenant-0.2.2.tar.gz
Algorithm Hash digest
SHA256 d5e1acf91c3c547a0325533396c208f150adf198a64dd2de2467713abf333122
MD5 5deb2dab36814faf5d1431637e36a1fb
BLAKE2b-256 16a36e0842988bc559b5a8d02b1f23fd1afcb2aa953783e1001c64f998cf529b

See more details on using hashes here.

File details

Details for the file django_omnitenant-0.2.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_omnitenant-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 443f79895cdf0602a1b44a4865adf4b67cda38b99b41e2d3b10623b2135d8ed7
MD5 43da8b88381d604c39399b1a591269fe
BLAKE2b-256 812cd81464cb2ef6f720612663efc2f9ddc2c2746a93f5732cfa8f7b20930cef

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page