|pypi| |actions| |codecov| |downloads| |maintainability| |black|
django_multisite2
=================
With ``django_multisite2`` a single instance of a Django project can serve multiple sites using a single settings file (multi-tenant). The current ``SITE_ID`` is extracted from the URL.
In ``settings``, the static ``SITE_ID`` is replaced with ``django_multisite2`` dynamic ``SiteID``::
# settings.py
SITE_ID = SiteID(default=1)
the dynamic ``SiteID`` behaves like an integer. When combined with ``django_multisite2`` middleware, ``SiteID`` will return the current ``SITE_ID`` based on the url. For example, each url below is an alias of the same server instance. With ``django_multisite2`` you might have something like this::
# https://harare.example.com
>>> from django.conf import settings
>>> settings.SITE_ID
10
# https://kampala.example.com
>>> from django.conf import settings
>>> settings.SITE_ID
20
Python 3.11+ Django 4.2+. New releases are cut from the ``main`` branch.
Older versions of Django are supported by the original `django-multisite`_ project.
Installation
============
Install with pip:
.. code-block::
pip install django-multisite2
Replace your ``SITE_ID`` in ``settings.py`` to:
.. code-block::
from multisite import SiteID
SITE_ID = SiteID(default=1)
add to INSTALLED_APPS:
.. code-block::
INSTALLED_APPS = [
...
'django.contrib.sites',
'multisite',
...
]
Edit settings.py MIDDLEWARE:
.. code-block::
MIDDLEWARE = (
...
'multisite.middleware.DynamicSiteMiddleware',
...
)
Using a custom cache
--------------------
Append to settings.py, in order to use a custom cache that can be
safely cleared::
# The cache connection to use for django-multisite.
# Default: 'default'
CACHE_MULTISITE_ALIAS = 'multisite'
# The cache key prefix that django-multisite should use.
# If not set, defaults to the KEY_PREFIX used in the defined
# CACHE_MULTISITE_ALIAS or the default cache (empty string if not set)
CACHE_MULTISITE_KEY_PREFIX = ''
If you have set CACHE\_MULTISITE\_ALIAS to a custom value, *e.g.*
``'multisite'``, add a separate backend to settings.py CACHES::
CACHES = {
'default': {
...
},
'multisite': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'TIMEOUT': 60 * 60 * 24, # 24 hours
...
},
}
Domain fallbacks
----------------
By default, if the domain name is unknown, multisite will respond with
an HTTP 404 Not Found error. To change this behaviour, add to
settings.py::
# The view function or class-based view that django-multisite will
# use when it cannot match the hostname with a Site. This can be
# the name of the function or the function itself.
# Default: None
MULTISITE_FALLBACK = 'django.views.generic.base.RedirectView
# Keyword arguments for the MULTISITE_FALLBACK view.
# Default: {}
MULTISITE_FALLBACK_KWARGS = {'url': 'http://example.com/',
'permanent': False}
Templates
---------
This feature has been removed in version 2.0.0.
If required, create template subdirectories for domain level templates (in a
location specified in settings.TEMPLATES['DIRS'].
Multisite's template loader will look for templates in folders with the names of
domains, such as::
templates/example.com
The template loader will also look for templates in a folder specified by the
optional MULTISITE_DEFAULT_TEMPLATE_DIR setting, e.g.::
templates/multisite_templates
Cross-domain cookie support
---------------------------
In order to support `cross-domain cookies`_ , for purposes like single-sign-on, prepend the following to the top of
settings.py MIDDLEWARE (MIDDLEWARE_CLASSES for Django < 1.10)::
MIDDLEWARE = (
'multisite.middleware.CookieDomainMiddleware',
...
)
CookieDomainMiddleware will consult the `Public Suffix List`_
for effective top-level domains.
It caches this file
in the system's default temporary directory
as ``effective_tld_names.dat``.
To change this in settings.py::
MULTISITE_PUBLIC_SUFFIX_LIST_CACHE = '/path/to/multisite_tld.dat'
By default,
any cookies without a domain set
will be reset to allow \*.domain.tld.
To change this in settings.py::
MULTISITE_COOKIE_DOMAIN_DEPTH = 1 # Allow only *.subdomain.domain.tld
In order to fetch a new version of the list,
run::
manage.py update_public_suffix_list
Post-migrate signal: post_migrate_sync_alias
--------------------------------------------
The ``post-migrate`` signal ``post_migrate_sync_alias`` is registered in the ``apps.py``. ``post_migrate_sync_alias``
ensures the ``domain`` in multisite's ``Alias`` model is updated to match that of django's ``Site`` model. This signal must
run AFTER any ``post-migrate`` signals that manipulate Django's ``Site`` model. If you have an app that manipulates Django's
``Site`` model, place it before ``multisite`` in `settings. INSTALLED_APPS`. If this is not possible, you may configure ``multisite``
to not connect the ``post-migrate`` signal in ``apps.py`` so that you can do it somewhere else in your code.
To configure `multisite` to not connect the `post-post_migrate_sync_alias` in the `apps.py`, update your settings::
MULTISITE_REGISTER_POST_MIGRATE_SYNC_ALIAS = False
With the `settings` attribute set to `False`, it is your responsibility to connect the signal in your code. Note that if you do not sync the `Alias` and `Site`
models after the `Site` model has changed, multisite may not recognize the domain and switch to the fallback view or
raise a `Http404` error.
Development Environments
------------------------
Multisite returns a valid Alias when in "development mode" (defaulting to the
alias associated with the default SiteID.
Development mode is either:
- Running tests, i.e. manage.py test
- Running locally in settings.DEBUG = True, where the hostname is a top-level name, i.e. localhost
In order to have multisite use aliases in local environments, add entries to
your local etc/hosts file to match aliases in your applications. E.g. ::
127.0.0.1 example.com
127.0.0.1 examplealias.com
And access your application at example.com:8000 or examplealias.com:8000 instead of
the usual localhost:8000.
Tests
-----
To run the tests::
python runtests.py
.. _django-multisite: https://github.com/ecometrica/django-multisite
.. _cross-domain cookies: http://en.wikipedia.org/wiki/HTTP_cookie#Domain_and_Path
.. _Public Suffix List: http://publicsuffix.org/
.. |pypi| image:: https://img.shields.io/pypi/v/django-multisite2.svg
:target: https://pypi.python.org/pypi/django-multisite2
.. |actions| image:: https://github.com/erikvw/django-multisite2/actions/workflows/build.yml/badge.svg
:target: https://github.com/erikvw/django-multisite2/actions/workflows/build.yml
.. |codecov| image:: https://codecov.io/gh/erikvw/django-multisite2/branch/develop/graph/badge.svg
:target: https://codecov.io/gh/erikvw/django-multisite2
.. |downloads| image:: https://pepy.tech/badge/django-multisite2
:target: https://pepy.tech/project/django-multisite2
.. |maintainability| image:: https://api.codeclimate.com/v1/badges/4992e131641fc6929b1a/maintainability
:target: https://codeclimate.com/github/erikvw/django-multisite2/maintainability
:alt: Maintainability
.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/ambv/black
:alt: Code Style
Raw data
{
"_id": null,
"home_page": null,
"name": "django-multisite2",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": "Erik van Widenfelt <ew2789@gmail.com>",
"keywords": "django, nginx, edc, clinical trials, research, data management, gunicorn, deployment",
"author": null,
"author_email": "Leonid S Shestera <leonid@shestera.ru>",
"download_url": "https://files.pythonhosted.org/packages/c8/c7/b41b822c2c00c585f35070621c0c3527388c9ba85f2c930d82d5917ee28e/django_multisite2-2.1.0.tar.gz",
"platform": null,
"description": "|pypi| |actions| |codecov| |downloads| |maintainability| |black|\n\n\n\ndjango_multisite2\n=================\n\nWith ``django_multisite2`` a single instance of a Django project can serve multiple sites using a single settings file (multi-tenant). The current ``SITE_ID`` is extracted from the URL.\n\nIn ``settings``, the static ``SITE_ID`` is replaced with ``django_multisite2`` dynamic ``SiteID``::\n\n # settings.py\n SITE_ID = SiteID(default=1)\n\nthe dynamic ``SiteID`` behaves like an integer. When combined with ``django_multisite2`` middleware, ``SiteID`` will return the current ``SITE_ID`` based on the url. For example, each url below is an alias of the same server instance. With ``django_multisite2`` you might have something like this::\n\n # https://harare.example.com\n >>> from django.conf import settings\n >>> settings.SITE_ID\n 10\n\n # https://kampala.example.com\n >>> from django.conf import settings\n >>> settings.SITE_ID\n 20\n\n\nPython 3.11+ Django 4.2+. New releases are cut from the ``main`` branch.\n\nOlder versions of Django are supported by the original `django-multisite`_ project.\n\n\nInstallation\n============\n\nInstall with pip:\n\n.. code-block::\n\n pip install django-multisite2\n\n\nReplace your ``SITE_ID`` in ``settings.py`` to:\n\n.. code-block::\n\n from multisite import SiteID\n SITE_ID = SiteID(default=1)\n\n\nadd to INSTALLED_APPS:\n\n.. code-block::\n\n INSTALLED_APPS = [\n ...\n 'django.contrib.sites',\n 'multisite',\n ...\n ]\n\n\nEdit settings.py MIDDLEWARE:\n\n.. code-block::\n\n MIDDLEWARE = (\n ...\n 'multisite.middleware.DynamicSiteMiddleware',\n ...\n )\n\n\nUsing a custom cache\n--------------------\nAppend to settings.py, in order to use a custom cache that can be\nsafely cleared::\n\n # The cache connection to use for django-multisite.\n # Default: 'default'\n CACHE_MULTISITE_ALIAS = 'multisite'\n\n # The cache key prefix that django-multisite should use.\n # If not set, defaults to the KEY_PREFIX used in the defined\n # CACHE_MULTISITE_ALIAS or the default cache (empty string if not set)\n CACHE_MULTISITE_KEY_PREFIX = ''\n\nIf you have set CACHE\\_MULTISITE\\_ALIAS to a custom value, *e.g.*\n``'multisite'``, add a separate backend to settings.py CACHES::\n\n CACHES = {\n 'default': {\n ...\n },\n 'multisite': {\n 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',\n 'TIMEOUT': 60 * 60 * 24, # 24 hours\n ...\n },\n }\n\n\nDomain fallbacks\n----------------\n\nBy default, if the domain name is unknown, multisite will respond with\nan HTTP 404 Not Found error. To change this behaviour, add to\nsettings.py::\n\n # The view function or class-based view that django-multisite will\n # use when it cannot match the hostname with a Site. This can be\n # the name of the function or the function itself.\n # Default: None\n MULTISITE_FALLBACK = 'django.views.generic.base.RedirectView\n\n # Keyword arguments for the MULTISITE_FALLBACK view.\n # Default: {}\n MULTISITE_FALLBACK_KWARGS = {'url': 'http://example.com/',\n 'permanent': False}\n\nTemplates\n---------\n\nThis feature has been removed in version 2.0.0.\n\nIf required, create template subdirectories for domain level templates (in a\nlocation specified in settings.TEMPLATES['DIRS'].\n\nMultisite's template loader will look for templates in folders with the names of\ndomains, such as::\n\n templates/example.com\n\n\nThe template loader will also look for templates in a folder specified by the\noptional MULTISITE_DEFAULT_TEMPLATE_DIR setting, e.g.::\n\n templates/multisite_templates\n\n\nCross-domain cookie support\n---------------------------\n\nIn order to support `cross-domain cookies`_ , for purposes like single-sign-on, prepend the following to the top of\nsettings.py MIDDLEWARE (MIDDLEWARE_CLASSES for Django < 1.10)::\n\n MIDDLEWARE = (\n 'multisite.middleware.CookieDomainMiddleware',\n ...\n )\n\nCookieDomainMiddleware will consult the `Public Suffix List`_\nfor effective top-level domains.\nIt caches this file\nin the system's default temporary directory\nas ``effective_tld_names.dat``.\nTo change this in settings.py::\n\n MULTISITE_PUBLIC_SUFFIX_LIST_CACHE = '/path/to/multisite_tld.dat'\n\nBy default,\nany cookies without a domain set\nwill be reset to allow \\*.domain.tld.\nTo change this in settings.py::\n\n MULTISITE_COOKIE_DOMAIN_DEPTH = 1 # Allow only *.subdomain.domain.tld\n\nIn order to fetch a new version of the list,\nrun::\n\n manage.py update_public_suffix_list\n\n\nPost-migrate signal: post_migrate_sync_alias\n--------------------------------------------\nThe ``post-migrate`` signal ``post_migrate_sync_alias`` is registered in the ``apps.py``. ``post_migrate_sync_alias``\nensures the ``domain`` in multisite's ``Alias`` model is updated to match that of django's ``Site`` model. This signal must\nrun AFTER any ``post-migrate`` signals that manipulate Django's ``Site`` model. If you have an app that manipulates Django's\n``Site`` model, place it before ``multisite`` in `settings. INSTALLED_APPS`. If this is not possible, you may configure ``multisite``\nto not connect the ``post-migrate`` signal in ``apps.py`` so that you can do it somewhere else in your code.\n\nTo configure `multisite` to not connect the `post-post_migrate_sync_alias` in the `apps.py`, update your settings::\n\n MULTISITE_REGISTER_POST_MIGRATE_SYNC_ALIAS = False\n\nWith the `settings` attribute set to `False`, it is your responsibility to connect the signal in your code. Note that if you do not sync the `Alias` and `Site`\nmodels after the `Site` model has changed, multisite may not recognize the domain and switch to the fallback view or\nraise a `Http404` error.\n\nDevelopment Environments\n------------------------\nMultisite returns a valid Alias when in \"development mode\" (defaulting to the\nalias associated with the default SiteID.\n\nDevelopment mode is either:\n - Running tests, i.e. manage.py test\n - Running locally in settings.DEBUG = True, where the hostname is a top-level name, i.e. localhost\n\nIn order to have multisite use aliases in local environments, add entries to\nyour local etc/hosts file to match aliases in your applications. E.g. ::\n\n 127.0.0.1 example.com\n 127.0.0.1 examplealias.com\n\nAnd access your application at example.com:8000 or examplealias.com:8000 instead of\nthe usual localhost:8000.\n\nTests\n-----\n\nTo run the tests::\n\n python runtests.py\n\n.. _django-multisite: https://github.com/ecometrica/django-multisite\n.. _cross-domain cookies: http://en.wikipedia.org/wiki/HTTP_cookie#Domain_and_Path\n.. _Public Suffix List: http://publicsuffix.org/\n\n.. |pypi| image:: https://img.shields.io/pypi/v/django-multisite2.svg\n :target: https://pypi.python.org/pypi/django-multisite2\n\n.. |actions| image:: https://github.com/erikvw/django-multisite2/actions/workflows/build.yml/badge.svg\n :target: https://github.com/erikvw/django-multisite2/actions/workflows/build.yml\n\n.. |codecov| image:: https://codecov.io/gh/erikvw/django-multisite2/branch/develop/graph/badge.svg\n :target: https://codecov.io/gh/erikvw/django-multisite2\n\n.. |downloads| image:: https://pepy.tech/badge/django-multisite2\n :target: https://pepy.tech/project/django-multisite2\n\n.. |maintainability| image:: https://api.codeclimate.com/v1/badges/4992e131641fc6929b1a/maintainability\n :target: https://codeclimate.com/github/erikvw/django-multisite2/maintainability\n :alt: Maintainability\n\n.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg\n :target: https://github.com/ambv/black\n :alt: Code Style\n\n",
"bugtrack_url": null,
"license": null,
"summary": "Serve multiple sites from a single Django application",
"version": "2.1.0",
"project_urls": {
"Changelog": "http://github.com/erikvw/django-multisite2/blob/main/CHANGES",
"Documentation": "http://github.com/erikvw/django-multisite2/blob/develop/README.rst",
"Homepage": "http://github.com/erikvw/django-multisite2",
"Repository": "http://github.com/erikvw/django-multisite2.git"
},
"split_keywords": [
"django",
" nginx",
" edc",
" clinical trials",
" research",
" data management",
" gunicorn",
" deployment"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "010232dda08f37a2955279204a01f3efe8cac69ac0ef298da0eb3eb0c2bfec16",
"md5": "4cc2c203550ca87151a8a84f45d6fc8d",
"sha256": "29b63f9ca6aca0f77199f1ef657b1c2a4ccb133ae574aec4f0db5995cc754824"
},
"downloads": -1,
"filename": "django_multisite2-2.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "4cc2c203550ca87151a8a84f45d6fc8d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 120810,
"upload_time": "2025-07-18T21:16:03",
"upload_time_iso_8601": "2025-07-18T21:16:03.023676Z",
"url": "https://files.pythonhosted.org/packages/01/02/32dda08f37a2955279204a01f3efe8cac69ac0ef298da0eb3eb0c2bfec16/django_multisite2-2.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "c8c7b41b822c2c00c585f35070621c0c3527388c9ba85f2c930d82d5917ee28e",
"md5": "c20ed2978f5fa593d674270c5a90bf66",
"sha256": "aeccb6fba824efb28678c2630b2e8223da2b53fff034c0b3f38e9d304921eb97"
},
"downloads": -1,
"filename": "django_multisite2-2.1.0.tar.gz",
"has_sig": false,
"md5_digest": "c20ed2978f5fa593d674270c5a90bf66",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 89545,
"upload_time": "2025-07-18T21:16:04",
"upload_time_iso_8601": "2025-07-18T21:16:04.068717Z",
"url": "https://files.pythonhosted.org/packages/c8/c7/b41b822c2c00c585f35070621c0c3527388c9ba85f2c930d82d5917ee28e/django_multisite2-2.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-18 21:16:04",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "erikvw",
"github_project": "django-multisite2",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"lcname": "django-multisite2"
}