{ "info": { "author": "Mike Wakerly", "author_email": "opensource@hoho.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy" ], "description": "\ndjango-db-multitenant\n=====================\n\nProvides a simple multi-tenancy solution for Django based on the concept\nof having a **single tenant per database**.\n\nThis application is still experimental, but is being used in production\nby the authors. Contributions and discussion are welcome.\n\n`Read the Changelog `__\n\nBackground\n----------\n\nMulti-tenancy is the ability to support multiple distinct datasets from\nthe same application server. Each dataset usually maps to a customer\n(the tenant) and is partially or fully partitioned from all other tenant\ndata.\n\nAmong the possible approaches are:\n\n- **Isolated approach**: Separate database per tenant.\n- **Semi-isolated approach**: Shared database, separate namespaces\n (PostgreSQL schemas) or table names/prefix per tenant.\n- **Shared approach**: Single database for all tenants. Each table has\n a column identifying the tenant for that row of data.\n\nThis application supports two backends, MySQL and PostgreSQL:\n\n- **With MySQL**, this application implements a variation of the **isolated approach**,\n each tenant has its **own database**, however their **connection details are\n shared** (such as password, database user).\n\n- **For PostgreSQL**, this application implements a **semi-isolated approach**,\n each tenant has its own schema and the connection details are shared via the\n public schema.\n\ndjango-db-multitenant makes it possible (even easy) to take a Django\napplication designed for a single tenant and use it with multiple\ntenants.\n\nOperation\n---------\n\nThe main technique is as follows:\n\n#. When a request first arrives, determine desired the tenant from the\n ``request`` object, and save it in thread-local storage.\n#. Later in the request, when a database cursor is accquired, issue an\n SQL ``USE `` for the desired tenant with MySQL\n or ``SET search_patch TO `` with PostgreSQL.\n\nStep 1 is accomplished by implementing a `mapper\nclass `__.\nYour mapper takes a request object and returns a database name or tenant\nname, using whatever logic you like (translate hostname, inspect a HTTP\nheader, etc). The mapper result is saved in thread-local storage for\nlater use.\n\nStep 2 determines whether the desired database or schema has already\nbeen selected, and is skipped if so. This is implemented using a thin\ndatabase backend\nwrapper `for MySQL `__ and\n`for PostgreSQL `__\nwhich must be set in ``settings.DATABASES`` as the backend.\n\nUsage\n-----\n\n1. Install\n~~~~~~~~~~\n\nInstall ``django-db-multitenant`` (or add it to your setup.py).\n\n::\n\n $ pip install django-db-multitenant\n\n2. Implement a mapper\n~~~~~~~~~~~~~~~~~~~~~\n\nYou must implement a subclass of\n`db_multitenant.mapper `__\nwhich determines the database name and cache prefix from the request.\n\nTo help you to write your mapper, the repository contains examples of mappers which extracts the hostname\nof URL to determine the tenant name (eg. in `https://foo.example.com/bar/`, `foo` will be the tenant name):\n\n- `mapper for MySQL `__,\n which uses a portion of the hostname directly as the database name.\n\n- `mapper for PostgreSQL `__,\n which uses a portion of the hostname as search path (schema). PostgreSQL\n allows complex setups with sharing of common tables (public accounts for example),\n see the comment in the mapper for more details.\n\n- `mapper for Redis `__,\n which looks up the tenant using the hostname, throwing a 404 if unrecognized.\n\nFeel free to copy an example mapper in your project then adjust it to your needs.\n\n3. Update settings.py\n~~~~~~~~~~~~~~~~~~~~~\n\nSet the multitenant mapper by specifying the full dotted path to your\nimplementation (in this example, `mapper` is the name of file `mapper.py`):\n\n.. code:: python\n\n MULTITENANT_MAPPER_CLASS = 'myapp.mapper.TenantMapper'\n\nInstall the multitenant middleware as the *first* middleware of the list (prior to Django\n1.10, you must use the ``MIDDLEWARE_CLASSES`` setting):\n\n.. code:: python\n\n MIDDLEWARE = [\n 'db_multitenant.middleware.MultiTenantMiddleware',\n ....\n ]\n\nChange your database backend to the multitenant wrapper:\n\n.. code:: python\n\n DATABASES = {\n 'default': {\n 'ENGINE': 'db_multitenant.db.backends.mysql',\n 'NAME': 'devnull',\n }\n }\n\n*Note*: the ``NAME`` is useless for MySQL but due to a current\nlimitation, the named database must exist. It may be empty and\nread-only.\n\nOr for PostgreSQL:\n\n.. code:: python\n\n DATABASES = {\n 'default': {\n 'ENGINE': 'db_multitenant.db.backends.postgresql',\n 'NAME': 'mydb',\n }\n }\n\nOptionally, add the multitenant helper ``KEY_FUNCTION`` to your cache\ndefinition, which will cause cache keys to be prefixed with the value of\n``mapper.get_cache_prefix``:\n\n.. code:: python\n\n CACHES = {\n 'default' : {\n 'LOCATION': '127.0.0.1:11211',\n 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',\n 'KEY_FUNCTION': 'db_multitenant.cache.helper.multitenant_key_func'\n }\n }\n\n4. Tests\n~~~~~~~~\n\nIf the tenant name of your application is extracted from the URL (as in the provided examples of\n`mappers `__), you can add\na host to your ``/etc/hosts`` such as ``foo.example.com`` to redirect to your localhost server.\n\nYou should add ``foo.example.com`` to ``ALLOWED_HOSTS`` list in your Django settings and just try\nto reach your application from your browser with ``http://foo.example.com:8000``.\n\nThe examples of mappers provide information about the way to create a tenant zone.\n\nManagement Commands\n-------------------\n\nIn order to use management commands (like ``migrate``) with the correct tenant,\ninject this little hack at the end of your ``settings.py``:\n\n.. code:: python\n\n from db_multitenant.utils import update_from_env\n update_from_env(database_settings=DATABASES['default'],\n cache_settings=CACHES['default'])\n\nIf you didn't set ``CACHES`` in your settings and you don't intend to use a cache system,\nyou don't have to pass the ``cache_settings`` argument to the function.\n\nYou can then export ``TENANT_DATABASE_NAME`` for MySQL or ``TENANT_NAME`` for PostgreSQL\nand ``TENANT_CACHE_PREFIX`` on the command line, for example:\n\n.. code:: bash\n\n $ TENANT_DATABASE_NAME=example.com ./manage.py migrate\n\nDon't forget to create the database (MySQL) or the required schema first (PostgreSQL).\n\nThat\u2019s it. Because django-db-multitenant does not define any models,\nthere\u2019s no need to add it to ``INSTALLED_APPS``.\n\nAdvantages and Limitations\n--------------------------\n\nThere is no one-size-fits-all solution for a data modeling problem such\nas multi-tenancy (see \u2018Alternatives\u2019).\n\nAdvantages\n~~~~~~~~~~\n\n- Compatibility: Your Django application doesn\u2019t need any awareness of\n multi-tenancy. Database-level tools (such as ``mysqldump`` or ``pgdump``)\n just work.\n- Isolation: One tenant, one database means there\u2019s no intermingling of\n tenant data (excepted if you share tables with PostgreSQL).\n- Simplicity: Your application schemas don\u2019t need to be cluttered with\n \u2018Tenant\u2019 foreign key relationships.\n- Should work well with Django 1.6 connection persistence and\n connection pooling.\n\nLimitations\n~~~~~~~~~~~\n\n- Unorthodox. Django does not expect this kind of dynamic database\n connection tinkering, and there could be unexpected bugs.\n- Limited isolation. Since the same DB credentials are used for all\n tenants, bugs in the mapper (or anywhere else in the app) could cause\n data corruption.\n- A valid database still needs to be specified in ``settings.DATABASE``\n for use when the connection is first established with MySQL (this should be\n fixed eventually).\n- Overhead: requests may add up to one extra query (the\n ``USE `` statement for MySQL or the ``SET search_path TO `` for PostgreSQL).\n\nAlternatives and Further Reading\n--------------------------------\n\n- `django-tenant-schemas `__\n implements a semi-isolated approach using PostgreSQL schemas (and\n inspired this project, as well as the \u2018Overview\u2019 section above).\n\nCredits and License\n-------------------\n\nCopyright 2013 mike wakerly (opensource@hoho.com)\n\nLicensed under the Apache License, Version 2.0 (the \u201cLicense\u201d); you may\nnot use this file except in compliance with the License. You may obtain\na copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \u201cAS IS\u201d BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n\n", "description_content_type": null, "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/mik3y/django-db-multitenant", "keywords": "django multitenant", "license": "Apache", "maintainer": "", "maintainer_email": "", "name": "django-db-multitenant", "package_url": "https://pypi.org/project/django-db-multitenant/", "platform": "", "project_url": "https://pypi.org/project/django-db-multitenant/", "project_urls": { "Homepage": "https://github.com/mik3y/django-db-multitenant" }, "release_url": "https://pypi.org/project/django-db-multitenant/0.3.2/", "requires_dist": [ "Django (>=1.8.0)" ], "requires_python": "", "summary": "Multitenant support for Django, using one tenant per database.", "version": "0.3.2" }, "last_serial": 3540012, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "9e79a5c8fb3965eb7532991cf06b05db", "sha256": "ef78e71459349e1e6f1dc400c04a21ac8b0447a4599f0246db97a2bcca3dd91f" }, "downloads": -1, "filename": "django-db-multitenant-0.1.0.tar.gz", "has_sig": false, "md5_digest": "9e79a5c8fb3965eb7532991cf06b05db", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6769, "upload_time": "2013-07-09T17:55:00", "url": "https://files.pythonhosted.org/packages/79/c7/c2611c7c2d2c09257f337b4c0c6a85fb2240af9db2332eff28cdc705ce75/django-db-multitenant-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "00b3fd0c733cbf1f5e104a776aa26173", "sha256": "46216609716753927f1b0b4e43114413923704cd3f6636956b986f37ff818efb" }, "downloads": -1, "filename": "django-db-multitenant-0.1.1.tar.gz", "has_sig": false, "md5_digest": "00b3fd0c733cbf1f5e104a776aa26173", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7065, "upload_time": "2013-07-24T21:18:33", "url": "https://files.pythonhosted.org/packages/2c/6e/592f28e8c51cd321dd60e54f6c415732e14c9a5c9beec93013b38df691fb/django-db-multitenant-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "1880e79ee99ea4da0d58c38cd188c9f0", "sha256": "b9d2ea0f11135f98b10f162d8311b664f1d4d4e60f10e532f0591b6045d4bda7" }, "downloads": -1, "filename": "django-db-multitenant-0.1.2.tar.gz", "has_sig": false, "md5_digest": "1880e79ee99ea4da0d58c38cd188c9f0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7146, "upload_time": "2014-05-14T22:22:50", "url": "https://files.pythonhosted.org/packages/58/6a/6fb26f1e5dbcb4880a10f5bfcf5aea802b52d3641c82cfad45b92f1fb03f/django-db-multitenant-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "2252c77e15de62779be27ebb157ec83b", "sha256": "1547c135bc77dea2ee7fc403bb2e971afc8a55cd06e3e09ebfb050a0cb9b727e" }, "downloads": -1, "filename": "django-db-multitenant-0.1.3.tar.gz", "has_sig": false, "md5_digest": "2252c77e15de62779be27ebb157ec83b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7414, "upload_time": "2016-09-10T15:59:04", "url": "https://files.pythonhosted.org/packages/16/d5/595d207d460ddcf68d0569608cdb159ca1443f550e194eebfc91aebf7e86/django-db-multitenant-0.1.3.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "deec2282d635944e95e927a892667705", "sha256": "afe3bf2d212651e34fc2b7ddf33b8b67a966695267ae53e6b2d46c3c1c473af9" }, "downloads": -1, "filename": "django_db_multitenant-0.3.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "deec2282d635944e95e927a892667705", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 21137, "upload_time": "2018-02-01T00:22:52", "url": "https://files.pythonhosted.org/packages/ce/6e/12b26ead2e06ade3a5ef04f53de0347c2407ac4aac9e0d5ac758ebe070be/django_db_multitenant-0.3.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "00b537b8f9ddeef43518e5d8fec7cfc1", "sha256": "26dd8cc52aee9039736b8b800fd180663e33d00bf61c30ac89c5a1d0781a363b" }, "downloads": -1, "filename": "django-db-multitenant-0.3.2.tar.gz", "has_sig": false, "md5_digest": "00b537b8f9ddeef43518e5d8fec7cfc1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19555, "upload_time": "2018-02-01T00:22:54", "url": "https://files.pythonhosted.org/packages/3d/5a/39854a8121bffdc36e4cea9a81ab86e77ed003f598f174663caad105c465/django-db-multitenant-0.3.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "deec2282d635944e95e927a892667705", "sha256": "afe3bf2d212651e34fc2b7ddf33b8b67a966695267ae53e6b2d46c3c1c473af9" }, "downloads": -1, "filename": "django_db_multitenant-0.3.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "deec2282d635944e95e927a892667705", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 21137, "upload_time": "2018-02-01T00:22:52", "url": "https://files.pythonhosted.org/packages/ce/6e/12b26ead2e06ade3a5ef04f53de0347c2407ac4aac9e0d5ac758ebe070be/django_db_multitenant-0.3.2-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "00b537b8f9ddeef43518e5d8fec7cfc1", "sha256": "26dd8cc52aee9039736b8b800fd180663e33d00bf61c30ac89c5a1d0781a363b" }, "downloads": -1, "filename": "django-db-multitenant-0.3.2.tar.gz", "has_sig": false, "md5_digest": "00b537b8f9ddeef43518e5d8fec7cfc1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 19555, "upload_time": "2018-02-01T00:22:54", "url": "https://files.pythonhosted.org/packages/3d/5a/39854a8121bffdc36e4cea9a81ab86e77ed003f598f174663caad105c465/django-db-multitenant-0.3.2.tar.gz" } ] }