{ "info": { "author": "Daniel Romaniuk", "author_email": "daniel.romaniuk@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", "Programming Language :: Python :: 2 :: Only", "Topic :: Utilities" ], "description": "This django app helps manage multi tenancy. Your web application can have several tenants, each with several users. \nThe users from one tenant are not allowed to see the data (model instances) that belong to another tenant. \n\nThis is done at the database table (django model) level. At the core is a model called Tenant, with only two fields: \nname and email. Any other model in your django project can be made \"tenant-aware\" by adding a ForeignKey field pointed at \nthat Tenant model.\n\ndjango-simple-multitenant helps reduce the amount of boilerplate code you need to make your models tenant-aware.\n\n\nHow to use\n==========\n\nModels\n------\nTo make a tenant-aware model, simply subclass django-multitenant's TenantModel.\nexample::\n\n\tfrom django.db import models\n\tfrom multitenant.models import TenantModel\n\n\tclass BugReport(TenantModel)\n\t\tdescription = models.CharField(max_length=200)\n\nTenantModels have a tenant-aware manager called tenant_objects::\n\n\tbugs = BugReport.tenant_objects.all()\n\nThis will bring up all instances owned by the \"current tenant\".\nThe current tenant, for a given request, is determined by checking the tenant field of the user profile for request.user.\nIf it's an anonymous user, the current tenant will be the base tenant. \nSee the base tenant section below for more information.\n\n\nForms\n-----\nFor any model that subclasses TenantModel, you'll want to use a TenantModelForm instead of django's ModelForm.\nThe TenantModelForm has two useful features:\n\n1. All ModelChoiceFields and ModelMultipleChoiceFields have their querysets filtered to show only the values for the current tenant.\n This happens during form class instantiation.\n2. The form's clean() method sets the instance's tenant field to that of the currently logged in user.\n\nexample::\n\n\tclass CompanyForm(TenantModelForm):\n\t class Meta:\n\t model = Company\n\nNote that we don't need to worry about filtering the options available for each form field.\n\t\n\nAdmin\n-----\nBy default, django-admin will show you all model instances. In a multitenant project, you might want to \n\"visit\" a tenant's account, and see just the instances that belong to them. If you use TenantAdmin as your\nModelAdmin class, you will see only the instances for the currently logged-in user (yourself).\n\nYou can then visit any tenant you please, by changing the Tenant linked to your own user profile.\n\nexample::\n\n\tfrom django.contrib import admin\n\tfrom multitenant.admin import TenantAdmin\n\tfrom myapp.models import *\n\t\n\tadmin.site.register(BugReport, TenantAdmin) \n\nUtilities\n---------\nTo verify that the current logged in tenant owns a particular instance::\n\n\tfrom multitenant.utils import current_tenant_owns_object\n\n\tif current_tenant_owns_object(obj):\n\t\tdo_something()\n\nA tenant-aware version of django's get_object_or_404 shortcut::\n\n\tfrom multitenant.utils import tenant_get_object_or_404\n\n\ttenant_get_object_or_404(BugReport, id=1)\n\nTo filter a queryset so that all instances belong to the currently logged in tenant::\n\n\tfrom multitenant.utils import tenant_filter\n\t\n\tbugs = BugReport.objects.all()\n\tbugs = tenant_filter(bugs)\n\nTo get the Tenant instance for the currently logged in tenant::\n\n\tfrom multitenant.middleware import get_current_tenant\n\n\ttenant = get_current_tenant()\n\nIn very rare instances, such as in django management commands, you might need to set the current tenant manually\nas there is no logged in user::\n\n\tfrom multitenant.middleware import set_current_tenant, set_tenant_to_default\n\n\tif val:\n\t\tset_current_tenant( Tenant.objects.get(id=val) )\n\telse:\n\t\tset_tenant_to_default()\n\t\n\nInstallation and Setup\n======================\n\nDjango apps\n-----------\nAdd django-multitenant to your list of installed apps:\nexample::\n\n\tINSTALLED_APPS = (\n\t 'django.contrib.auth',\n\t 'django.contrib.contenttypes',\n\t 'django.contrib.sessions',\n\t 'django.contrib.sites',\n\t 'django.contrib.messages',\n\t 'django.contrib.staticfiles',\n\t 'multitenant',\n )\t\n\t\nUser Profile\n------------\nYou must have a \"user profile\" model, and it must subclass TenantModel. \nThis is the django model that you use to extend auth.User, the one pointed to by AUTH_PROFILE_MODULE in your settings.py file; for a\ncomplete discussion see https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users\n\nexample::\n\n\tclass UserProfile(TenantModel):\n\t user = models.OneToOneField(User)\n\nBase tenant\n-----------\nThe first tenant (id=1) is called the \"base tenant\", and should be read-only. It is not used by regular users.\nThis is where you set up all the tenant-aware model instances for a new, empty tenant account. Now, when you create a new tenant, say with id=2,\nthis clones all the instances from the base tenant.\n\nexample, say you have a model called BugReportType. You may want each tenant to have their own set of custom BugReportTypes. When you\nfirst create a tenant, they need a decent set of values to start with.\nSet up a few starting values, for the base tenant (id=1)::\n\n\tmysql> select * from multitenant_tenant;\n\t+----+-------------------------+---------------------+\n\t| id | name | email |\n\t+----+-------------------------+---------------------+\n\t| 1 | Base tenant (read-only) | example@example.com |\n\t+----+-------------------------+---------------------+\n\t\t\n\tmysql> select * from bugs_bugreporttype;\n\t+-----+-----------+---------+\n\t| id | tenant_id | name | \n\t+-----+-----------+---------+\n\t| 1 | 1 | Closed |\n\t| 2 | 1 | In Work |\n\t+-----+-----------+---------+\n\nWhat happens when we create a new tenant? The base tenant gets cloned::\n\n\tmysql> select * from multitenant_tenant;\n\t+----+-------------------------+---------------------+\n\t| id | name | email |\n\t+----+-------------------------+---------------------+\n\t| 1 | Base tenant (read-only) | example@example.com |\n\t| 1 | Acme | example@acme |\n\t+----+-------------------------+---------------------+\n\t\t\n\tmysql> select * from bugs_bugreporttype;\n\t+-----+-----------+---------+\n\t| id | tenant_id | name | \n\t+-----+-----------+---------+\n\t| 1 | 1 | Closed |\n\t| 2 | 1 | In Work |\n\t| 3 | 2 | Closed |\n\t| 4 | 2 | In Work |\n\t+-----+-----------+---------+\n\nSo you should set up a base tenant with a starting set of values for all the tenant-aware models in your project.\n\n\nSpecial Considerations and Warnings\n===================================\nUniqueness constraints\n----------------------\nAdd the tenant field to any uniqueness constraints for tenant-aware models; \nremember that more than one tenant is now sharing the same database table.\nexample::\n\n\tunique_together = ((\"name\", \"tenant\"), (\"code\", \"tenant\"),)\n\nDefault values\n--------------\nBe careful with default values for ForeignKey or model fields. You don't want the default \n\nbad example::\n\n\tclass BugReport(TenantModel)\n\t bug_type = models.ForeignKey(\n\t BugReportType, \n\t on_delete = models.SET_DEFAULT,\n\t default = BugReportType.tenant_objects.get(name='New')\n\t )\n\nThat's a bad example because it depends on the current tenant being known while the BugReport\nclass is declared. It's far better to use a callable (function) as default value.\n\nbetter example::\n\n\tclass BugReport(TenantModel)\n\t bug_type = models.ForeignKey(\n\t BugReportType, \n\t on_delete = models.SET_DEFAULT,\n\t default = get_default_bugreporttype\n\t )\n\t\t\t\n def get_default_bugreporttype():\n return BugReportType.tenant_objects.get(name='New')\n\nDifficulty with bootstrapping the database\n------------------------------------------\nWhen you first run syncdb with the multitenant app installed, you may run into a chicken-and-egg problem with the user profile model class. \nThe user profile model must subclass TenantModel; it has a foreign key relation to Tenant. To create a new user profile, you must first create\na Tenant instance.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/phugoid/django-simple-multitenant", "keywords": null, "license": "LICENSE.txt", "maintainer": null, "maintainer_email": null, "name": "django-simple-multitenant", "package_url": "https://pypi.org/project/django-simple-multitenant/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/django-simple-multitenant/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/phugoid/django-simple-multitenant" }, "release_url": "https://pypi.org/project/django-simple-multitenant/0.1.0/", "requires_dist": null, "requires_python": null, "summary": "Helps manage multi tenancy for django projects", "version": "0.1.0" }, "last_serial": 790635, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "b63c8a0e2f32466805ff0b3e33a6e041", "sha256": "e9d6eeb33ad0723f9a9c573de2fe9d9ab083c37a93e7b57660bfda92ea89a150" }, "downloads": -1, "filename": "django-simple-multitenant-0.1.0.tar.gz", "has_sig": false, "md5_digest": "b63c8a0e2f32466805ff0b3e33a6e041", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8526, "upload_time": "2011-12-30T16:00:49", "url": "https://files.pythonhosted.org/packages/2c/6e/4035cdd03a890ac541aab3396fe110c0a989e44a8d7d534d1427aab5e316/django-simple-multitenant-0.1.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "b63c8a0e2f32466805ff0b3e33a6e041", "sha256": "e9d6eeb33ad0723f9a9c573de2fe9d9ab083c37a93e7b57660bfda92ea89a150" }, "downloads": -1, "filename": "django-simple-multitenant-0.1.0.tar.gz", "has_sig": false, "md5_digest": "b63c8a0e2f32466805ff0b3e33a6e041", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8526, "upload_time": "2011-12-30T16:00:49", "url": "https://files.pythonhosted.org/packages/2c/6e/4035cdd03a890ac541aab3396fe110c0a989e44a8d7d534d1427aab5e316/django-simple-multitenant-0.1.0.tar.gz" } ] }