Git Product home page Git Product logo

django-multisite's Introduction

https://travis-ci.org/ecometrica/django-multisite.svg?branch=master https://coveralls.io/repos/github/ecometrica/django-multisite/badge.svg?branch=master

README

Install with pip:

pip install django-multisite

Or get the code via git:

git clone git://github.com/ecometrica/django-multisite.git django-multisite

Then run:

python setup.py install

Or add the django-multisite/multisite folder to your PYTHONPATH.

If you wish to contribute, instead run:

python setup.py develop

Quickstart

Replace your SITE_ID in settings.py to:

from multisite import SiteID
SITE_ID = SiteID(default=1)

Add these to your INSTALLED_APPS:

INSTALLED_APPS = [
    ...
    'django.contrib.sites',
    'multisite',
    ...
]

Add to your settings.py TEMPLATES loaders in the OPTIONS section:

TEMPLATES = [
    ...
    {
        ...
        'DIRS': {...}
        'OPTIONS': {
            'loaders': (
                'multisite.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
            )
        }
        ...
    }
    ...
]

Edit settings.py MIDDLEWARE (MIDDLEWARE_CLASSES for Django < 1.10):

MIDDLEWARE = (
    ...
    'multisite.middleware.DynamicSiteMiddleware',
    ...
)

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
        ...
    },
}

Multisite determines the ALLOWED_HOSTS by checking all Alias domains. You can also set the MULTISITE_EXTRA_HOSTS to include additional hosts. This can include wildcards.:

MULTISITE_EXTRA_HOSTS = ['example.com']
# will match the single additional host

MULTISITE_EXTRA_HOSTS = ['.example.com']
# will match any host ending '.example.com'

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.

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

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 cookies

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

Tests

To run the tests:

python setup.py test

Or:

pytest

Before deploying a change, to verify it has not broken anything by running:

tox

This runs the tests under every supported combination of Django and Python.

django-multisite's People

Contributors

avbasov avatar brunoprograma avatar coddingtonbear avatar eddiejessup avatar erikvw avatar jbazik avatar jedie avatar jordanreiter avatar jordiecometrica avatar mghughes avatar pauricthelodger avatar plazix avatar rebkwok avatar rory-geoghegan-ecometrica avatar sfllaw avatar shestera avatar vit-ivanov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-multisite's Issues

Django 3.2 checks for an integer `SITE_ID`

Django 3.2 has introduced a new check in the sites framework which ensures that the SITE_ID setting has an integer value.

django.contrib.sites.checks

def check_site_id(app_configs, **kwargs):
    if (
        hasattr(settings, 'SITE_ID') and
        not isinstance(settings.SITE_ID, (type(None), int))
    ):
        return [
            Error('The SITE_ID setting must be an integer', id='sites.E101'),
        ]
    return []

It's a fairly straightforward function so would be easy to monkey-patch, but has anybody else got any thoughts on this problem?

Make django-multisite location independant

Hi,

the actual code of the application enforce the 'installation' in the actual project directory, where it can find the 'templates' one.
It should be interesting to be able to install django-multisite systemwide or anywhere in the PYTHONPATH without this restriction.

Please look at the following patch which i think solves the problem (using TEMPLATE_DIRS[0]):

--- a/multisite/template_loader.py
+++ b/multisite/template_loader.py
@@ -5,7 +5,7 @@ from django.contrib.sites.models import Site
from django.conf import settings

def get_template_sources(template_name, template_dirs=None):
-    template_dir = os.path.join(os.path.normpath(os.path.dirname(__file__)), '..', 'templates', Site.objects.get_current().domain)
+    template_dir = os.path.join(settings.TEMPLATE_DIRS[0], Site.objects.get_current().domain)
    try:
      yield safe_join(template_dir, template_name)
    except UnicodeDecodeError:

Unable to migrate from empty database with the template loader included

I've created a brand new project & come to the initial migration to start my database.

django-multisite is included with what I'd call the "basic" setup, with the following settings;

INSTALLED_APPS = [
    
    # Django
    'django.forms',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.flatpages',
    'django.contrib.gis',
    'django.contrib.humanize',
    'django.contrib.messages',
    'django.contrib.redirects',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.staticfiles',

    'multisite',

    'project',
]


MIDDLEWARE = [
    'multisite.middleware.DynamicSiteMiddleware',
    # everything else follows...
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'project', 'templates'),
        ],
        'OPTIONS': {
            'context_processors': [
                ...
            ],
            'loaders': [
                'multisite.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader'
            ],
        },
    },
]

Running migrate with an empty database, the project appears to raise the following with it's attempt to access the Site table seemingly from the following flow;

https://github.com/ecometrica/django-multisite/blob/master/multisite/template/loaders/filesystem.py#L15
https://github.com/ecometrica/django-multisite/blob/master/multisite/hacks.py#L47

(.venv) mwalker@mymac my-project % python manage.py migrate
Traceback (most recent call last):
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedTable: relation "django_site" does not exist
LINE 1: ..."django_site"."domain", "django_site"."name" FROM "django_si...
                                                             ^


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "manage.py", line 31, in <module>
    main()
  File "manage.py", line 27, in main
    execute_from_command_line(sys.argv)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/core/management/__init__.py", line 357, in execute
    django.setup()
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/apps/registry.py", line 122, in populate
    app_config.ready()
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/cms/apps.py", line 12, in ready
    setup()
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/cms/utils/setup.py", line 40, in setup
    plugin_pool.validate_templates()
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/cms/plugin_pool.py", line 83, in validate_templates
    loader.get_template(template)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/template/loader.py", line 15, in get_template
    return engine.get_template(template_name)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/template/backends/django.py", line 34, in get_template
    return Template(self.engine.get_template(template_name), self)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/template/engine.py", line 143, in get_template
    template, origin = self.find_template(template_name)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/template/engine.py", line 125, in find_template
    template = loader.get_template(name, skip=skip)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/template/loaders/base.py", line 18, in get_template
    for origin in self.get_template_sources(template_name):
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/multisite/template/loaders/filesystem.py", line 15, in get_template_sources
    domain = Site.objects.get_current().domain
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/contrib/sites/models.py", line 58, in get_current
    return self._get_site_by_id(site_id)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/multisite/hacks.py", line 47, in SiteManager_get_site_by_id
    site = self.get(pk=site_id)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 402, in get
    num = len(clone)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 256, in __len__
    self._fetch_all()
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 1242, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/models/query.py", line 55, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1140, in execute_sql
    cursor.execute(sql, params)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 99, in execute
    return super().execute(sql, params)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 76, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/Users/mwalker/Sites/my-project/.venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.ProgrammingError: relation "django_site" does not exist
LINE 1: ..."django_site"."domain", "django_site"."name" FROM "django_si...
                                                             ^

I'm fairly new to this project, but it would appear that there might be potential for exception handling in SiteManager_get_site_by_id to deal with ProgrammingError on the assumption that it's an initial migration perhaps?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.