Code


Version 15 (modified by grahamd, 5 years ago) (diff)

Add warning about where settings file located until this example is fixed properly.

How to use django with mod_wsgi

This is a very simple recipe to deploy a django application with mod_wsgi. This procedure has been tested on Windows but should work on any operating system supported by apache, mod_wsgi, and django.

Installation

The mod_wsgi package can be downloaded from http://code.google.com/p/modwsgi/. Ensure that you work through the mod_wsgi installation and configuration instructions before trying to setup Django. For links to Windows binaries and steps on setting up mod_wsgi Windows see its separate installation instructions but still refer to main installation instructions to work out if mod_wsgi was installed correctly.

If wanting to know about source code reloading issues when running Django under Apache and mod_wsgi then ensure you read document about reloading on mod_wsgi site. Do be aware that reloading your application by touching the WSGI script file only works for mod_wsgi daemon mode on UNIX/Apache 2.X systems. That specific reloading feature is not available on Windows, Apache 1.3 or if using embedded mode with Apache 2.X. If mod_wsgi daemon mode is available, you can also set it up to automatically restart on any code change. This is covered in mod_wsgi documentation on reloading, but is made clearer in this blog entry. This will give you same sort of automatic reloading capability as the Django development server.

Be aware that on UNIX systems you have the choice of using mod_wsgi in embedded mode or daemon mode. If you are not adept at configuring Apache and do not know how to tune Apache MPM settings to suit a specific type of application, then make sure you use mod_wsgi daemon mode. This is because the default Apache MPM settings are for static file serving and PHP, they are not suitable for large persistent Python web applications. If you use embedded mode and don't change the settings you are likely to have problems with running out of memory on memory constrained systems and see load spikes which may cripple your system. These are the same problems that can arise as when using mod_python and you similarly haven't tuned the Apache MPM settings. You will need to delve into the documentation on the mod_wsgi site for how to use daemon mode.

Note that the mod_wsgi site provides its own documentation for integrating Django with mod_wsgi. Those instructions go into areas this document does not and in some respect should be seen as being a more definitive source of information. It is also a good idea to read through other documentation on the mod_wsgi site as well, especially in respect to installation, configuration or application issues. Reading the other documentation on the mod_wsgi site will save you a lot of time if you do have any issues as it is quite comprehensive.

Configuration of Apache and mod_wsgi

In this section I will take you through an example, the django application is called dj_survey. This application is part of a project called "dj_project".

The urls.py I used to serve the application with the Django built-in development server:

from django.conf.urls.defaults import *
from django.contrib import databrowse
import registration 


urlpatterns = patterns('',
    # Example:
    # (r'^dj_project/', include('dj_project.foo.urls')),

    # Uncomment this for admin:
    (r'^admin/', include('django.contrib.admin.urls')),
    (r'^dj_survey/', include('dj_project.dj_survey.urls')),
    (r'^databrowse/(.*)', databrowse.site.root),
    (r'^accounts/', include('registration.urls')),
    (r'^yui/(?P<path>.*)$', 'django.views.static.serve', {'document_root': '../../svn_views/yui/build','show_indexes': True}),
    (r'^site_media/(?P<path>.*)$', 'django.views.static.serve',{'document_root': './media','show_indexes': True}),
)

In httpd.conf you should load the mod_wsgi and include the file containing the configuration of your django application. The trap there is all the path should use "/" and not "\".

httpd.conf

#This should be included somewhere at the top of this file
LoadModule wsgi_module modules/mod_wsgi.so

#somewhere at the bottom
Include "<PATH TO YOUR DJANGO PROJECT>/apache/apache_django_wsgi.conf"

NOTE: Putting settings file in the 'apache' subdirectory where the WSGI script file is located is technically a security risk. This is because in order for the WSGI script file to be served from there, you have had to configure Apache to tell it it can use that directory. In doing that, it can technically serve files from there as static files. Thus, if Apache was incorrectly configured and files in that directory made accessible via a URL as static files, it would be possible to download the settings file. Since the settings file can contain database passwords, that obviously isn't going to be a good thing to happen. This example really should be modified, with settings files located one directory up from where they are, as would normally be the case.

This suppose that you have created a folder named apache in your django project in this folder your should add the following files:

08/16/2007  04:12 PM             1,082 apache_django_wsgi.conf
08/16/2007  04:31 PM               557 dj_survey.wsgi
08/16/2007  04:31 PM             4,362 settings_production.py
08/16/2007  04:09 PM               712 urls_production.py
08/16/2007  04:33 PM                 0 __init__.py

Note: There is a file called __init__.py in <PATH TO YOUR DJANGO PROJECT>/apache. In this case <PATH TO YOUR DJANGO PROJECT> is equal to c:\<LONG PATH>\workspace\dj_project

Like in the httpd.conf you should pay attention to the separator. You should use "/" and not "\"

apache_django_wsgi.conf

Alias /site_media/ "<PATH TO YOUR DJANGO PROJECT>/media/"
<Directory "<PATH TO YOUR DJANGO PROJECT>/media">
Order allow,deny
Options Indexes
Allow from all
IndexOptions FancyIndexing
</Directory>

Alias /yui/ "<PATH TO YOUR YUI>/build/"
<Directory "<PATH TO YOUR YUI>/build">
Order allow,deny
Options Indexes
Allow from all
IndexOptions FancyIndexing
</Directory>

Alias /media/ "<PATH TO YOUR DJANGO SRC>/trunk/django/contrib/admin/media/"
<Directory "<PATH TO YOUR DJANGO SRC>/trunk/django/contrib/admin/media">
Order allow,deny
Options Indexes
Allow from all
IndexOptions FancyIndexing
</Directory>


WSGIScriptAlias / "<PATH TO YOUR DJANGO PROJECT>/apache/dj_survey.wsgi"

<Directory "<PATH TO YOUR DJANGO PROJECT>/apache">
Allow from all
</Directory>

Now here it is the core of the wsgi application. The path there should use "
"as separator.

dj_survey.wsgi

import os, sys

#Calculate the path based on the location of the WSGI script.
apache_configuration= os.path.dirname(__file__)
project = os.path.dirname(apache_configuration)
workspace = os.path.dirname(project)
sys.path.append(workspace) 

#Add the path to 3rd party django application and to django itself.
sys.path.append('C:\\yml\\_myScript_\\dj_things\\web_development\\svn_views\\django_src\\trunk')
sys.path.append('C:\\yml\\_myScript_\\dj_things\\web_development\\svn_views\\django-registration')

os.environ['DJANGO_SETTINGS_MODULE'] = 'dj_project.apache.settings_production'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Note: If you need to write something in error.log located in the following folder <Your Apache Installation>\Apache2.2\logs you can insert the line below in your WSGI file. In our example this file is called dj_survey.wsgi. This method is very convenient to get the PYTHONPATH correct:

print >> sys.stderr, sys.path 

In this file you are defining the settings that will be used by your Django application, in our example this file is called: dj_project.apache.settings_production (Note: ".py" is implicit and should not be added). There is nothing special in that file except that I am pointing to a special urls.py which reflect the configuration I am using in production.

ROOT_URLCONF = 'dj_project.apache.urls_production'

urls_production.py

from django.conf.urls.defaults import *
from django.contrib import databrowse
import registration 


urlpatterns = patterns('',
    # Example:
    # (r'^dj_project/', include('dj_project.foo.urls')),

    # Uncomment this for admin:
    (r'^admin/', include('django.contrib.admin.urls')),
    (r'^dj_survey/', include('dj_project.dj_survey.urls')),
    (r'^databrowse/(.*)', databrowse.site.root),
    (r'^accounts/', include('registration.urls')),
)

Test

Restart apache and now you should be able to enjoy your application served by Apache and mod_wsgi.

Additional Tweaking

If you're taking advantage of the great Internationalization features of Django you may come across a curious problem. Namely, uploading of non-ascii filenames with the Django storage system with the default apache settings on most systems will trigger UnicodeEncodeError exceptions when calling functions like os.path(). To avoid these issues, ensure that the following lines are included in your apache envvars file (typically found in /etc/apache2/envvars).

export LANG='en_US.UTF-8'
export LC_ALL='en_US.UTF-8'

This error likely wont rear its head during development on the test server as, when run from the command line, the ./manage.py script inherits the users language and locale settings.

References