This post aims to provide a quick overview on how to mount a Django project at a different location than the webserver root. Although the Django development guide suggests that Django websites should always be rooted at /, having a project located at a sub-URL might be necessary when multiple Django projects need to be run on the same webserver (provided that we don’t want to — or simply can’t — create a virtual host for each project).

First off, we need to change the Apache configuration file appropriately. (Interestingly enough, Apache configuration files are recursively included in one another so, no matter in which file we choose to add a new line, Apache will see it. The right place to go when we want to find and modify site-specific information is the sites-available folder.) We probably have a line that looks like this:


WSGIScriptAlias / /path/to/django/project/django.wsgi

This is telling Apache that URLs matching the given prefix / (i.e., all URLs) should be handled by mod_wsgi and passed over to Django. We want to change this behaviour, so that only URLs matching a specific sub-URL (like myserver.mydomain.com/myproject) will be seen by Apache as related to our Django Project:


WSGIScriptAlias /myproject /path/to/django/project/django.wsgi

As for the webserver, this is it. Once the new configuration has been loaded, URLs starting with myserver.mydomain.com/myproject will point to our Django project. These URLs are not passed to Django as they are. The first part matching the WSGIScriptAlias prefix is stripped off by Apache and stored in Python’s environment variable collection, under the SCRIPT_NAME key (i.e., environ[‘SCRIPT_NAME’]). This mechanism makes Django unaware of the specific location where the project is mounted within the webserver.

Hence, if all applications are correctly implemented, the project should work at the new URL right “out-of-the-box”. Unfortunately, this is sometimes not the case. In particular, hard-coded URLs will no longer work. Thus, references among views based on view URLs will be broken. We can manually update all URLs to fix this (until we change the location of our project again…). Or we can save the hassle by avoiding URLs when referencing views and use Django’s URL-reversing capabilities:

  • from within a view, the reverse() method can be used:
    from django.core.urlresolvers import reversedef myView(request):
    return HttpResponseRedirect(reverse('myproject.views.myOtherView'))
    
    
  • from within a template, the url tag serves the same purpose:
    {% url myProject.views.myViews %}

One thing is left that does not work right away, the login. If we used the @login_required decorator in some of our views, Django will automatically redirect the user to the login page if he/she is not currently logged in to our website. By default, Django will assume that the login page lives in the root folder of the webserver. We can instruct it to point to the correct address by adding the following line to our settings.py:


LOGIN_URL = myserver.mydomain.com/myproject/accounts/login

However, we must still manually update this line if we change the URL of our website again. In order to (be super-cool people and) have Django automatically adjust its login URL to the current mount point, we can remove the line we just added from settings.py and modify django.wsgi as follows:


import os
import sys
import myproject.settings
import django.core.handlers.wsgi

sys.path.append('/srv/www/myproject/')
path = '/srv/www'
if path not in sys.path:
sys.path.insert(0, '/srv/www')
os.environ['DJANGO_SETTINGS_MODULE'] = 'myproject.settings'
_application = django.core.handlers.wsgi.WSGIHandler()

def application(environ, start_response):
myproject.settings.LOGIN_URL = environ['SCRIPT_NAME'] + '/accounts/login'
return _application(environ, start_response)

We now have a Django project rooted in a subpath on our webserver. If we need to move it again to a different URL, all we need to do is change the WSGIScriptAlias prefix.

Advertisements