Posted by Paul on 17th February 2014
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
execute_from_command_line(sys.argv)
- 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
Posted by Paul on 5th February 2014
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...
</VirtualHost>
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)