Locating Django Applications in Their Own Sub-Directory

It’s straightforward to keep Django applications in their own directory if necessary – though the convention is to place them in the project’s root directory.

Instructions below assume pwd/$PWD is the project’s root directory.

  • Create the directory in which Django applications will be located:
$ mkdir apps
  • Create a new application named _newapp_ under apps/newapp using startapp‘s optional directory parameter (this requires that the target directory exist, so create it first):
$ mkdir apps/newapp
 $ ./manage.py startapp newapp apps/newapp
  • Modify the project’s manage.py file to ensure the development server can find the applications. It should end up looking something like the following:
#!/usr/bin/env python
 import os
 import sys

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")

    # Add the apps directoriy to Python's path. In production it will
    # be necessary to add the apps directory to the path, too.
    from os.path import abspath, dirname, join
    PROJECT_ROOT = abspath(dirname(__file__))
    sys.path.append(join(PROJECT_ROOT, "apps"))

    from django.core.management import execute_from_command_line

  • For a development environment, that’s it. For production, ensure the apps directory is included on the project’s PYTHONPATH. If using Apache and mod_wsgi then ensure that the WSGIDaemonProcess directive in the project’s <virtualhost/> includes the path to the newly added apps directory, as well as the path to the (virtualenv’s) python site packages directory and the project’s root directory:
WSGIDaemonProcess myapp python-path=/myapp/apps:/myapp/python-packages:/myapp

Django, X-SendFile and Apache

Static File Access Management

Django documentation advises against using an application to directly serve static files when in a production environment. A dedicated HTTP server, optimised for serving static files, should be used for this purpose. That’s fine for serving publicly accessible files. But serving static files which require user access permission checking necessarily involves routing the download request via application code.

It’s possible to get the best of both Django view processing, where access permissions can be managed and dedicated HTTP server download handling, by using a HTTP server extension that processes response headers applied by the web application. The mod_xsendfile Apache module┬áis one such module. Used with django-sendfile, application management of file downloads can be cleanly separated into interface and implementation.

django-xsendfile and Apache mod_xsendfile Installation

Install the mod_xsendfile module:

$ sudo apt-get install libapache2-mod-xsendfile

The mod_xsendfile module is usually enabled by the install scripts. Use a2enmod to further manage its enabled status.

Enable X-SENDFILE header processing within the application’s Apache virtual host. For instance, a virtual host for for a Django application to serve example.com, /etc/apache2/sites-available/example.com:

<VirtualHost *:80>
    ServerName example.com

    # mod_wsgi settings
    WSGIDaemonProcess example python-path=/var/www/example.com/app
    WSGIProcessGroup example
    WSGIScriptAlias / /var/www/example.com/app/example/wsgi.py

    # Publicly available static files directly available via Apache.
    Alias /static /var/www/example.com/app/static
    Alias /pub-uploads /var/www/example.com/pub-uploads

    # Restricted access files via Apache mod_xsendfile.
    XSendFile On
    XSendFilePath /var/www/example.com/priv-uploads

    # Apache <directory/> directives here...

From the Django application:

$ pip install django-xsendfile

Within the Django application’s settings.py:

SENDFILE_BACKEND = 'sendfile.backends.xsendfile'

Conventionally this can be overridden in a local_settings.py and imported by settings.py for a development environments:

SENDFILE_BACKEND = 'sendfile.backends.development'

The Django view used to manage download access permissions may then look something like the following:

from django.views.generic.base import View
from sendfile import sendfile

class DownloadFile(View):
    def get(self, request):
    # Get user access rights and the file's file-system path.
    # ...
    # If access denied return HttpResponseForbidden(), else:
    return sendfile(request, file_path)

The sendfile API also provides response header management, such as Content-Disposition header content:

def sendfile(request, filename,
    attachment=False, attachment_filename=None,
    mimetype=None, encoding=None)
Tags: , ,