3

I use a custom middleware for mapping subdomains to applications' urls.py by assigning the relevant urls.py to the request.urlconf variable.

This works fine, with the exception of the {% url %} template tag.
I'm getting a NoReverseMatch and can't figure out why.
The debug page shows that the reverse function receives a value in the view_name parameter, so it should work.

This happens to every {% url %} tag in the template.
If I switch to folder-like urls (eg myproject.com/sub, instead of sub.myproject.com), the tags work fine.

Any ideas on why this happens and how can this be fixed are much appreciated.

user1102018
  • 4,369
  • 6
  • 26
  • 33
  • You might want to look into https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts - havent done it.. just a thought – karthikr May 12 '13 at 20:33
  • @karthikr, thanks. I read through this page, but nothing there to solve the problem. – user1102018 May 12 '13 at 20:44

1 Answers1

2

Well, after not finding what I needed, I came to the following solution. I discarded the request.urlconf mapping and went for rewriting request.path_info.
I'm posting my solution here, in case someone runs into this problem.

First step, add the following middleware to your project:

class SubdomainMiddleware:
    """Subdomain for Django"""
    def process_request(self, request):
        domain_parts = request.get_host().lower().replace('www.', '').split('.example.com', 1)
        if len(domain_parts) > 1 and domain_parts[0]:
            subdomain = domain_parts[0]
        else:
            subdomain = None

        if subdomain:
            if request.path_info[-1] != '/':
                request.path_info += '/'
            request.path_info = '/%s%s' % (subdomain, request.path_info)

Next, add the following code that overrides the reverse function:

from django.conf import settings
from django.core import urlresolvers

_reverse = urlresolvers.reverse

def reverse(*args, **kwargs):
    # In case reversing a full url
    if args[0].startswith('http'):
        return args[0]
    # In case reversing a url name
    if '/' not in args[0]:
        url = _reverse(*args, **kwargs)
    else:
        # In case reversing a url path
        url = args[0]

    parts = url.strip('/').split('/', 1)
    subdomain = parts[0]
    path = parts[1] if len(parts) > 1 else ''
    protocol = 'http://' if settings.DEBUG else 'https://'
    return '%s%s%s/%s' % (protocol, subdomain, '.example.com', path)

urlresolvers.reverse = reverse

I placed it in the same file the custom middleware sits.

That's it!

As far as I know and tested, everything works: reversing, redirecting, template {% url %} tags etc.

Note:
I made three assumptions in this code:

  • The domain name is example.com. Change it as you need, place it in settings.py, whatever suits you.
  • The subdomain maps to a subfolder. E.g. sub.example.com will be converted to example.com/sub.
  • My site uses SSL anywhere. So I simply check for settings.DEBUG value in the custom reverse function to see if it should use http or https.

Hope it helps.

user1102018
  • 4,369
  • 6
  • 26
  • 33