{"id":13625612,"url":"https://github.com/citusdata/django-multitenant","last_synced_at":"2025-05-15T03:07:12.337Z","repository":{"id":39169614,"uuid":"91768585","full_name":"citusdata/django-multitenant","owner":"citusdata","description":"Python/Django support for distributed multi-tenant databases like Postgres+Citus","archived":false,"fork":false,"pushed_at":"2024-07-17T11:22:36.000Z","size":624,"stargazers_count":765,"open_issues_count":64,"forks_count":123,"subscribers_count":41,"default_branch":"main","last_synced_at":"2025-05-12T03:54:42.041Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/citusdata.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-19T05:23:40.000Z","updated_at":"2025-05-07T07:58:26.000Z","dependencies_parsed_at":"2024-04-30T09:59:06.670Z","dependency_job_id":"e8b8f0d6-64f9-4c7f-af38-79fb828185c9","html_url":"https://github.com/citusdata/django-multitenant","commit_stats":{"total_commits":233,"total_committers":20,"mean_commits":11.65,"dds":0.4721030042918455,"last_synced_commit":"8b9af1c23bfdba2a2aedd9c8b4dbed5b539ba5eb"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citusdata%2Fdjango-multitenant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citusdata%2Fdjango-multitenant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citusdata%2Fdjango-multitenant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/citusdata%2Fdjango-multitenant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/citusdata","download_url":"https://codeload.github.com/citusdata/django-multitenant/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253908957,"owners_count":21982685,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T21:01:58.328Z","updated_at":"2025-05-15T03:07:07.327Z","avatar_url":"https://github.com/citusdata.png","language":"Python","readme":"# django-multitenant \u003cbr\u003e[![Build Status](https://github.com/citusdata/django-multitenant/actions/workflows/django-multitenant-tests.yml/badge.svg)](https://github.com/citusdata/django-multitenant/actions/workflows/django-multitenant-tests.yml) [![Latest Documentation Status](https://readthedocs.org/projects/django-multitenant/badge/?version=latest)](https://django-multitenant.readthedocs.io/en/latest/?badge=latest)  [![Coverage Status](https://codecov.io/gh/citusdata/django-multitenant/branch/main/graph/badge.svg?token=taRgoSgHUw)](https://codecov.io/gh/citusdata/django-multitenant) [![PyPI Version](https://badge.fury.io/py/django-multitenant.svg)](https://badge.fury.io/py/django-multitenant)\n\nPython/Django support for distributed multi-tenant databases like Postgres+Citus\n\nEnables easy scale-out by adding the tenant context to your queries, enabling the database (e.g. Citus) to efficiently route queries to the right database node.\n\nThere are architecures for building multi-tenant databases viz. **Create one database per tenant**, **Create one schema per tenant** and **Have all tenants share the same table(s)**. This library is based on the 3rd design i.e **Have all tenants share the same table(s)**, it assumes that all the tenant relates models/tables have a tenant_id column for representing a tenant.\n\nThe following link talks more about the trade-offs on when and how to choose the right architecture for your multi-tenant database:\n\nhttps://www.citusdata.com/blog/2016/10/03/designing-your-saas-database-for-high-scalability/\n\nThe following blogpost is a good starting point to start to use django-multitenant\nhttps://www.citusdata.com/blog/2023/05/09/evolving-django-multitenant-to-build-scalable-saas-apps-on-postgres-and-citus/\n\n**Other useful links on multi-tenancy**:\n1. https://www.citusdata.com/blog/2017/03/09/multi-tenant-sharding-tutorial/\n1. https://www.citusdata.com/blog/2017/06/02/scaling-complex-sql-transactions/\n1. https://www.youtube.com/watch?v=RKSwjaZKXL0\n\n\n## Installation:\n1. `pip install  --no-cache-dir django_multitenant`\n\n## Supported Django versions/Pre-requisites.\n\n| Python                | Django        |Citus          |\n| ----------------------| --------------|---------------|\n| 3.8 3.9 3.10 3.11     | 4.2           | 11  12        |\n| 3.8 3.9 3.10 3.11     | 4.1           | 11  12        |\n| 3.8 3.9 3.10 3.11     | 4.0           | 10  11  12    |\n| 3.7                   | 3.2           | 10  11  12    |\n\n\n\n\n## Usage:\n\nIn order to use this library you can either use Mixins or have your models inherit from our custom model class.\n\n\n### Changes in Models:\n1. In whichever files you want to use the library import it:\n   ```python\n   from django_multitenant.fields import *\n   from django_multitenant.models import *\n   ```\n2. All models should inherit the TenantModel class.\n   `Ex: class Product(TenantModel):`\n3. Define a static variable named tenant_id and specify the tenant column using this variable.You can define tenant_id in three ways. Any of them is acceptable \n   * Using TenantMeta.tenant_field_name variable\n   * Using TenantMeta.tenant_id variable\n   * Using tenant_id field\n   \u003cbr\u003e\n\n\n   \u003e **Warning**\n   \u003e Using  tenant_id field directly in the class is not suggested since it may cause collision if class has a field named with 'tenant'\n   \u003cbr\u003e\n\n4. All foreign keys to TenantModel subclasses should use TenantForeignKey in place of\n   models.ForeignKey\n5. A sample model implementing the above 2 steps:\n   ```python\n   class Store(TenantModel):\n     name =  models.CharField(max_length=50)\n     address = models.CharField(max_length=255)\n     email = models.CharField(max_length=50)\n     class TenantMeta:\n       tenant_field_name = \"id\"\n \n   class Product(TenantModel):\n     store = models.ForeignKey(Store)\n     name = models.CharField(max_length=255)\n     description = models.TextField()\n     class Meta:\n       unique_together = [\"id\", \"store\"]\n     class TenantMeta:\n       tenant_field_name = \"store_id\"\n   class Purchase(TenantModel):\n     store = models.ForeignKey(Store)\n     product_purchased = TenantForeignKey(Product)\n     class TenantMeta:\n       tenant_field_name = \"store_id\"\n   ```\n\n\n### Changes in Models using mixins:\n1. In whichever files you want to use the library import it by just saying \n   ```python\n   from django_multitenant.mixins import *\n   ```\n1. All models should use the `TenantModelMixin` and the django `models.Model` or your customer Model class\n   `Ex: class Product(TenantModelMixin, models.Model):`\n1. Define a static variable named tenant_id and specify the tenant column using this variable.\n   `Ex: tenant_id='store_id'`\n1. All foreign keys to TenantModel subclasses should use TenantForeignKey in place of\n   models.ForeignKey\n1. Referenced table in TenantForeignKey should include a unique key including tenant_id and primary key\n   ```\n   Ex:       \n   class Meta:\n        unique_together = [\"id\", \"store\"]\n   ```\n1. A sample model implementing the above 3 steps:\n   ```python\n\n   class ProductManager(TenantManagerMixin, models.Manager):\n     pass\n \n   class Product(TenantModelMixin, models.Model):\n     store = models.ForeignKey(Store)\n     tenant_id='store_id'\n     name = models.CharField(max_length=255)\n     description = models.TextField()\n \n     objects = ProductManager()\n \n     class Meta:\n       unique_together = [\"id\", \"store\"]\n \n   class PurchaseManager(TenantManagerMixin, models.Manager):\n     pass\n \n   class Purchase(TenantModelMixin, models.Model):\n     store = models.ForeignKey(Store)\n     tenant_id='store_id'\n     product_purchased = TenantForeignKey(Product)\n \n     objects = PurchaseManager()\n   ```\n\n\n\n### Automating composite foreign keys at db layer:\n1. Creating foreign keys between tenant related models using TenantForeignKey would automate adding tenant_id to reference queries (ex. product.purchases) and join queries (ex. product__name). If you want to ensure to create composite foreign keys (with tenant_id) at the db layer, you should change the database ENGINE in the settings.py to `django_multitenant.backends.postgresql`.\n  ```python\n    'default': {\n        'ENGINE': 'django_multitenant.backends.postgresql',\n        ......\n        ......\n        ......\n  }\n  ```\n### Where to Set the Tenant?\n1. Write authentication logic using a middleware which also sets/unsets a tenant for each session/request. This way developers need not worry about setting a tenant on a per view basis. Just set it while authentication and the library would ensure the rest (adding tenant_id filters to the queries). A sample implementation of the above is as follows:\n   ```python\n       from django_multitenant.utils import set_current_tenant\n       \n       class MultitenantMiddleware:\n           def __init__(self, get_response):\n               self.get_response = get_response\n\n           def __call__(self, request):\n               if request.user and not request.user.is_anonymous:\n                   set_current_tenant(request.user.employee.company)\n               return self.get_response(request)\n   ```\n   \n   In your settings, you will need to update the `MIDDLEWARE` setting to include the one you created.\n   ```python\n      MIDDLEWARE = [\n          # ...\n          # existing items\n          # ...\n          'appname.middleware.MultitenantMiddleware'\n      ]\n   ```\n2. Set the tenant using set_current_tenant(t) api in all the views which you want to be scoped based on tenant. This would scope all the django API calls automatically(without specifying explicit filters) to a single tenant. If the current_tenant is not set, then the default/native API  without tenant scoping is used.\n   ```python\n    def application_function:\n      # current_tenant can be stored as a SESSION variable when a user logs in.\n      # This should be done by the app\n      t = current_tenant\n      #set the tenant\n      set_current_tenant(t);\n      #Django ORM API calls;\n      #Command 1;\n      #Command 2;\n      #Command 3;\n      #Command 4;\n      #Command 5;\n   ```\n\n## Supported APIs:\n1. Most of the APIs under Model.objects.*.\n1. Model.save() injects tenant_id for tenant inherited models.\n   ```python\n   s=Store.objects.all()[0]\n   set_current_tenant(s)\n   \n   #All the below API calls would add suitable tenant filters.\n   #Simple get_queryset()\n   Product.objects.get_queryset()\n   \n   #Simple join\n   Purchase.objects.filter(id=1).filter(store__name='The Awesome Store').filter(product__description='All products are awesome')\n   \n   #Update\n   Purchase.objects.filter(id=1).update(id=1)\n   \n   #Save\n   p=Product(8,1,'Awesome Shoe','These shoes are awesome')\n   p.save()\n   \n   #Simple aggregates\n   Product.objects.count()\n   Product.objects.filter(store__name='The Awesome Store').count()\n   \n   #Subqueries\n   Product.objects.filter(name='Awesome Shoe');\n   Purchase.objects.filter(product__in=p);\n\n   ```\n\n## Credits\n\nThis library uses similar logic of setting/getting tenant object as in [django-simple-multitenant](https://github.com/pombredanne/django-simple-multitenant). We thank the authors for their efforts.\n\n## License\n\nCopyright (C) 2023, Citus Data\nLicensed under the MIT license, see LICENSE file for details.\n","funding_links":[],"categories":["Python"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcitusdata%2Fdjango-multitenant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcitusdata%2Fdjango-multitenant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcitusdata%2Fdjango-multitenant/lists"}