Multi-Tenancy ============= Mezzanine makes use of Django's ``sites`` app to support multiple sites in a single project. This functionality is always "turned on" in Mezzanine: a single ``Site`` record always exists, and is referenced when retrieving site related data, which most content in Mezzanine falls under. Where Mezzanine diverges from Django is how the ``Site`` record is retrieved. Typically a running instance of a Django project is bound to a single site defined by the :django:setting:`SITE_ID` setting, so while a project may contain support for multiple sites, a separate running instance of the project is required per site. Mezzanine uses a pipeline of checks to determine which site to reference when accessing content. The most important of these is the one where the host name of the current request is compared to the domain name specified for each ``Site`` record. With this in place, true multi-tenancy is achieved, and multiple sites can be hosted within a single running instance of the project. Here's the list of checks in the pipeline, in order: * The session variable ``site_id``. This allows a project to include features where a user's session is explicitly associated with a site. Mezzanine uses this in its admin to allow admin users to switch between sites to manage, while accessing the admin on a single domain. * The domain matching the host of the current request, as described above. * The environment variable ``MEZZANINE_SITE_ID``. This allows developers to specify the site for contexts outside of a HTTP request, such as management commands. Mezzanine includes a custom ``manage.py`` which will check for (and remove) a ``--site=ID`` argument. * Finally, Mezzanine will fall back to the :django:setting:`SITE_ID` setting if none of the above checks can occur. Per-site Themes --------------- Consider a project for an organization or business with several related domains that are to be managed by the same people, or for which sharing of resources is a big benefit. These related domains can share the same Django process, which can offer easier management and reduced resource needs in the server environment. The domains involved could have a direct subsidiary relationship, as with example.com and several subdomains, or they may be completely separate domains, as with example.com, example2.com, example3.com. Either way, the domains are different hosts to which themes may be independently associated using the :ref:`HOST_THEMES` setting:: # For a main domain and several subdomains. HOST_THEMES = [('example.com', 'example_theme'), ('something.example.com', 'something_theme'), ('somethingelse.example.com', 'somethingelse_theme')] # or for separate domains, HOST_THEMES = [('www.example.com', 'example_theme'), ('example.com', 'example_theme'), ('www.example2.com', 'example2_theme'), ('www.example3.com', 'example3_theme')] In either :ref:`HOST_THEMES` example above, there are three themes. Let's continue with the second case, for example.com, example2.com, and example3.com, for which there are three theme apps constructed: ``example_theme``, ``example2_theme``, and ``example3_theme``. The following treatment illustrates a kind of theme inheritance across the domains, with ``example_theme`` serving as the "base" theme. Suppose ``example_theme`` contains a dedicated page content type (see :ref:`creating-custom-content-types`), we'll call it the ``HomePage``, which is given a model definition in ``example_theme/models.py``, along with any other theme-related custom content definitions. Here is the layout for ``example_theme``, the "base" theme in this arrangement:: /example_theme admin.py models.py <-- has a HomePage type, subclassing Page ... /static /css /img /js /templates /base.html <-- used by this and the other two themes /pages /index.html <-- for the HomePage content type /templatetags some_tags.py <-- for code supporting HomePage functionality The second and third themes, ``example2_theme`` and ``example3_theme``, could be just as expansive, or they could be much simplified, as shown by this layout for ``example2_theme`` (``example3_theme`` could be identical):: /example2_theme /templates /pages /index.html <-- for the HomePage content type Each theme would be listed under the :django:setting:`INSTALLED_APPS` setting, with the "base" theme, ``example_theme``, listed first. The project's main ``urls.py`` would need the following line active, so that "/" is the target URL Mezzanine finds for home page rendering (via the ``HomePage`` content type):: url("^$", "mezzanine.pages.views.page", {"slug": "/"}, name="home"), Mezzanine will look for a page instance at '/' for each theme. ``HomePage`` instances would be created via the admin system for each site, and given the URL of '/' under the "Meta data" URL field. (Log in to /admin, pick each site, in turn, creating a ``HomePage`` instance, and editing the "Meta data" URL of each). Although these aren't the only commands involved, they are useful during the development process:: * ``python manage.py startapp theme`` - start a theme; add/edit files next; add to INSTALLED_APPS before restart * ``python manage.py syncdb --migrate`` - after changes to themes; could require writing migrations * ``python manage.py collectstatic`` - gather static resources from the themes on occasion Finally, under /admin, these sites will share some resources, such as the media library, while there is separation of content stored in the database (independent ``HomePage`` instances, independant blog posts, an independent page hierarchy, etc.). Furthermore, the content types added to, say ``example_theme``, e.g. ``HomePage``, are shared and available in the different sites. Such nuances of sharing must be considered when employing this approach. Upgrading from ``TemplateForHostMiddleware`` -------------------------------------------- Mezzanine implements host-specific templates using a template loader since version 4.3. Prior to that, the ``TemplateForHostMiddleware`` was used. If you are upgrading from a version lower than 4.3 and getting warnings in the terminal about ``TemplateForHostMiddleware``, edit your ``settings.py`` to switch to the new loader-based approach: * Remove ``TemplateForHostMiddleware`` from your ``MIDDLEWARE`` or ``MIDDLEWARE_CLASSES`` setting. * Remove ``"APP_DIRS": True`` from your ``TEMPLATES`` setting. * Add ``mezzanine.template.loaders.host_themes.Loader`` to the list of template loaders. Your ``TEMPLATES`` setting should look like this (notice the ``"loaders"`` key): .. code:: python TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [...], "OPTIONS": { "context_processors": [...], "builtins": [...], "loaders": [ "mezzanine.template.loaders.host_themes.Loader", "django.template.loaders.filesystem.Loader", "django.template.loaders.app_directories.Loader", ] }, }, ]