Bob
Bob

Reputation: 6173

django multiple domains, single app

I am trying to set my Django app work with multiple domains (while serving slightly different content) I wrote this middleware:

class MultiSiteMiddleware(object):
    def process_request(self, request):
        host = request.get_host()
        host_part = host.split(':')[0].split('.com')[0].split('.')
        host = host_part[len(host_part)-1] + '.com'
        site = Site.objects.get(domain=host)
        settings.SITE_ID = site.id
        settings.CURRENT_HOST = host
        Site.objects.clear_cache()
        return

In views I use this:

def get_site(request):
    current_site = get_current_site(request)
    return current_site.name

def view(request, pk):
    site = get_site(request)

    if site == 'site1':
        # serve content1
        ...
    elif site == 'site2'
        # serve content2
        ...

But now there are 404 errors (I sometimes find them in logs, don't see them while browsing my site manually) where they aren't supposed to be, like my site sometimes is serving content for wrong domains, can they happen because of some flaw in the above middleware and view code or I should look somewhere else?

Upvotes: 9

Views: 14918

Answers (5)

YPCrumble
YPCrumble

Reputation: 28722

Django's Sites framework has built-in middleware to accomplish this.

Simply enable the Sites framework and add this to your MIDDLEWARE:

'django.contrib.sites.middleware.CurrentSiteMiddleware'

This automatically passes a request object to Site.objects.get_current() on every request. It solves your problem by giving you access to request.site on every request.

For reference, the code as of 1.11 is:

from django.utils.deprecation import MiddlewareMixin

from .shortcuts import get_current_site


class CurrentSiteMiddleware(MiddlewareMixin):
    """
    Middleware that sets `site` attribute to request object.
    """

    def process_request(self, request):
        request.site = get_current_site(request)

Upvotes: 3

Spoutnik16
Spoutnik16

Reputation: 864

Serve multiple domain from one website (django1.8 python3+)

The goal is, obviously, to serve multiple domains, from one Django instance. That means, we use the same models, the same database, the same logic, the same views, the same templates, but to serve different things.

Searching the interwebs, I came to the idea of using the sites framework. The sites framework was designed to do exactly that thing. In fact, the sites framework has been used to do exactly that. But I haven't been able to know on which version of Django it was, and actually, I came to the idea the sites framework was just a vestigial module. Basically, it is just a table, with SITE_ID and SITE_URL, that you can easily access with a function. But I've been unable to find how, from that, you can do a multi-domain website.

So my idea has been, how to modify the url resolver. The idea behind all these is easy : www.domain1.com/bar is resolved to /domain1/bar, and www.domain2.foo is resolved to /domain2/foo. This solves the problem because, if you want to serve multiple domains, you just have to serve multiple folders.

In Django, to achieve this, you have to modify two things : * the way Django routes requests * the way django write urls

The way Django routes requests

Django routes requests with middlewares. That's it. So we just have to write a middleware that re-route requests.

To make it easier, middlewares can have a process_request method that process requests (WOW), before requests are handled. So let's write a DomainNameMiddleware

#!python
#app/middleware.py

class DomaineNameMiddleware:
"""
change the request path to add the domain_name at the first
"""
    def process_request(self, request):
        #first, we split the domain name, and take the part before the extension
        request_domain = request.META['HTTP_HOST'].split('.')[-2]
        request.path_info = "/%s/%s" % (request_domain, request.path.split('/')[1:])

The way Django writes URL

When I'm talking about django writing url, i'm principally thinking of the {% url %} template tag, the get_absolute_url methods, the resolve and resolve_lazy functions, and those basic Django thing. If we rewrite the way Django handle url, we have to tell Django to write url that way.

But basically it's pretty easy, thanks to Django.

You can easily rewrite basic Django function by just rewriting them, typically in init.py files of modules you added as apps. So :

#!python
#anyapp/__init__.py

from django.core import urlresolvers

old_reverse = urlresolvers.reverse

def new_reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None):
    """
    return an url with the first folder as a domain name in .com
    """
    TLD = 'com'
    old_reverse_url = old_reverse(viewname, urlconf, args, kwargs, current_app)

    # admin will add itself everytime you reload an admin page, so we have to delete it
    if current_app == 'admin':
        return '/%s' % old_reverse_url[len('admin'):].replace('adminadmin', 'admin')

    return '//%s.%s/%s' % (app, TLD, path)

How to use it ?

I use it with base urls.py as dispatchedr.

#!python
#urls.py
from django.conf.urls import include, url

from domain1 import urls as domain1_urls
from domain2 import urls as domain2_urls

urlpatterns = [
    url(r'^domain1/', include(domain1_urls, namespace='domain1')),
    url(r'^domain2/', include(domain2_urls, namespace='domain2)),
    ]

Upvotes: 7

Carlos Calla
Carlos Calla

Reputation: 6716

Try using the "sites" framework in django to get the domain name. You already know this I guess.

Take a look here: https://docs.djangoproject.com/en/1.7/ref/contrib/sites/#getting-the-current-domain-for-full-urls

See this:

>>> Site.objects.get_current().domain
'example.com'

Without the https://www or http://www.. Probably your domains will end in .org or some country .pe .ru etc not just .com.

There might be a case when people don't point to your domain but to your IP address for some reason, maybe development of testing so you should always raise an exception with Site.DoesNotExist

Upvotes: 0

Stuart Leigh
Stuart Leigh

Reputation: 856

I had a similar requirement and decided not to use the django sites framework. My middleware looks like

class MultiSiteMiddleware(object):

    def process_request(self, request):
        try:
            domain = request.get_host().split(":")[0]
            request.site = Site.objects.get(domain=domain)
        except Site.DoesNotExist:
            return http.HttpResponseNotFound()

then my views have access to request.site

If you're seeing 404's for sites that aren't yours in your logs it would seem like somebody has pointed their domain at your servers IP address, you could use apache/nginx to filter these out before they hit your app, but your middleware should catch them (though possibly by raising an uncaught 500 error instead of a 404)

Upvotes: 11

Prashant Gaur
Prashant Gaur

Reputation: 9828

I will suggest you to use django-multisite .It will fulfill your requirement.

Upvotes: 0

Related Questions