mod_wsgi / Django integration problems

5 views
Skip to first unread message

Giles Thomas

unread,
Oct 27, 2008, 5:01:52 PM10/27/08
to mod...@googlegroups.com
Hi all,

I'm trying to get mod_wsgi working with Django (1.0), and I've hit a
problem that I can't seem to be able to solve. It's simple enough that
I'm sure the solution is obvious and I'm just being stupid, but I've not
been able to find anything that works, even after several hours
searching and trying alternatives :-( Any help for a struggling
mod_wsgi newbie would be much appreciated!

The situation is: I have a simple app. It works in Django's test
server, and is meant to serve from three base URLs - let's call them
/admin, /foo, and /bar. My url.py therefore looks something like this:

-----------------------------------------------
urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^foo/', another.handler),
(r'^bar/', yet.another.handler)
)
-----------------------------------------------

In the dev server, these are available as /admin, /foo, and /bar.

Now, for production, I want Apache to be serving the root (for example,
the static pages located at /d, /e and /f) but I want /admin, /foo, and
/bar to go to Django via mod_wsgi. After some experimentation, and
after reference to the documentation [1] I hit upon this Apache config:

-----------------------------------------------
WSGIScriptAlias /admin /path/to/my/wsgifile/config.wsgi
WSGIScriptAlias /foo /path/to/my/wsgifile/config.wsgi
WSGIScriptAlias /bar /path/to/my/wsgifile/config.wsgi
<Directory /path/to/my/wsgifile/>
Order deny,allow
Allow from all
</Directory>
-----------------------------------------------

The problem with this is that although access to /admin, /foo and /bar
was being delegated to Django correctly, the URL that was passed to
Django had the /admin (or whatever) stripped off - so its URL matching
did not work. So, I tried using some code from the documentation to add
it back in - in config.wsgi, I put (with mysite.settings and the
sys.path.append changed, of course):

-----------------------------------------------
import os, sys
sys.path.append('/usr/local/django')
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

import django.core.handlers.wsgi

_application = django.core.handlers.wsgi.WSGIHandler()

def application(environ, start_response):
environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO']
return _application(environ, start_response)
-----------------------------------------------

I was unsure about this, as it looked like it was for versions of Django
prior to 1.0, and anyway seemed to be a workaround for a now-fixed
Django bug - but it seemed to work, until I tried to log in using
Django's admin interface, under /admin. When I did that, the login page
appeared correctly, but logging in tried to redirect me to
/admin/admin/admin. After looking at a few other examples, my best
guess is that pages that did redirects were somehow appending an extra
copy of the SCRIPT_NAME at the start of the URL.

I'm not sure what to make of all this but I guess my modified
config.wsgi is completely wrong...

Is that correct? And if so, what is the right way to configure mod_wsgi
so that it handles these "root-level" URLs but also passes them down to
the Django URL resolver?

Any help would be much appreciated!


Cheers,

Giles


[1] http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango

--
Giles Thomas
MD & CTO, Resolver Systems Ltd.
giles....@resolversystems.com
+44 (0) 20 7253 6372

Try out Resolver One! <http://www.resolversystems.com/get-it/>

17a Clerkenwell Road, London EC1M 5RD, UK
VAT No.: GB 893 5643 79
Registered in England and Wales as company number 5467329.
Registered address: 843 Finchley Road, London NW11 8NA, UK


Mike Beaumont

unread,
Oct 27, 2008, 6:26:37 PM10/27/08
to mod...@googlegroups.com
Your problem is this. You're prepending SCRIPTNAME to PATHINFO each time so it adds another /admin for every successive request. What I suggest doing is adding 3 Location directives in your apache configuration for /admin /foo and /bar. But if you want it done the way you have set up, create some middleware that acts on each request to set environ['PATHINFO'] equal to environ['SOMEVAR'] that you set in the .wsgi script as environ['SOMEVAR'] = environ['SCRIPTNAME'] + environ['PATHINFO']. This way, you application will know the exact path regardless of the previous request instead of adding the previous request.

Ask any questions you have, please, because I wrote this up kinda quick.
--
Michael Beaumont

Graham Dumpleton

unread,
Oct 28, 2008, 2:50:28 AM10/28/08
to mod...@googlegroups.com
Two changes required to what you have done. See below.

2008/10/28 Giles Thomas <giles....@resolversystems.com>:

Add within this Directory block:

WSGIApplicationGroup myapp

This will force all URLs to be handled within same Python sub
interpreter within the process. Without it, each WSGIScriptAlias mount
point would see a separate Python sub interpreter created for that
subset of URLs.

If this is the only Python web application you are running via
mod_wsgi, probably better to make it the main Python interpreter by
using:

WSGIApplicationGroup %{GLOBAL}

> Order deny,allow
> Allow from all
> </Directory>
> -----------------------------------------------
>
> The problem with this is that although access to /admin, /foo and /bar
> was being delegated to Django correctly, the URL that was passed to
> Django had the /admin (or whatever) stripped off - so its URL matching
> did not work. So, I tried using some code from the documentation to add
> it back in - in config.wsgi, I put (with mysite.settings and the
> sys.path.append changed, of course):
>
> -----------------------------------------------
> import os, sys
> sys.path.append('/usr/local/django')
> os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'
>
> import django.core.handlers.wsgi
>
> _application = django.core.handlers.wsgi.WSGIHandler()
>
> def application(environ, start_response):
> environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO']
> return _application(environ, start_response)

Close but not quite. You need to wipe out value of SCRIPT_NAME. Try:

def application(environ, start_response):
environ['PATH_INFO'] = environ['SCRIPT_NAME'] + environ['PATH_INFO']

environ['SCRIPT_NAME'] = ''
return _application(environ, start_response)

If you are using Django 1.0, this should be enough to make it happy.

> -----------------------------------------------
>
> I was unsure about this, as it looked like it was for versions of Django
> prior to 1.0, and anyway seemed to be a workaround for a now-fixed
> Django bug - but it seemed to work, until I tried to log in using
> Django's admin interface, under /admin. When I did that, the login page
> appeared correctly, but logging in tried to redirect me to
> /admin/admin/admin. After looking at a few other examples, my best
> guess is that pages that did redirects were somehow appending an extra
> copy of the SCRIPT_NAME at the start of the URL.
>
> I'm not sure what to make of all this but I guess my modified
> config.wsgi is completely wrong...
>
> Is that correct? And if so, what is the right way to configure mod_wsgi
> so that it handles these "root-level" URLs but also passes them down to
> the Django URL resolver?
>
> Any help would be much appreciated!

This is actually a hard one in some ways. Normal use case would be to
have root handled by Python web application, but then overlay other
URLs against static resources etc. Here you only want Python web
application to appear to be root, but not really, as only want to
expose specific URLs.

There may actually be other ways of doing this but would need to think
about. It might be able to be done using mod_rewrite rules in a tricky
way, but that could be more trouble than its worth and may still
require a fiddle in the WSGI script file.

If I come up with a way that doesn't require the fiddle in the WSGI
script file will let you know.

Graham

Graham Dumpleton

unread,
Oct 28, 2008, 2:52:54 AM10/28/08
to mod...@googlegroups.com
2008/10/28 Mike Beaumont <mjb...@gmail.com>:

> Your problem is this. You're prepending SCRIPTNAME to PATHINFO each time so
> it adds another /admin for every successive request. What I suggest doing is
> adding 3 Location directives in your apache configuration for /admin /foo
> and /bar.

Not sure what you have in mind for what will be in the Location
directives. The mod_wsgi package doesn't generally rely on Location
directives for mapping URLs to an application, ie., in the style of
mod_python, as that approach has various problems.

Care to elaborate? May help me to come up with a better way of doing
it that doesn't involve a fiddle in the WSGI script file.

Graham

Graham Dumpleton

unread,
Oct 28, 2008, 4:39:16 AM10/28/08
to mod...@googlegroups.com
Hmmm, I'm stupid, this is really easy to do, can do it for all three
URLs in one line and with no fiddle to WSGI script file.

More later when validate the solution. :-)

Graham

2008/10/28 Graham Dumpleton <graham.d...@gmail.com>:

Graham Dumpleton

unread,
Oct 28, 2008, 5:05:05 AM10/28/08
to mod...@googlegroups.com
Okay, here it is. All you need in the Apache configuration file is:

WSGIScriptAliasMatch ^/(admin|foo|bar)(/.*)?$
/Users/grahamd/Sites/echo.wsgi/$1$2

You do not need to make any changes to the WSGI script file. Just use:

import os, sys
sys.path.append('/usr/local/django')
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()

You don't need the WSGIApplicationGroup either as when using
WSGIScriptAliasMatch like this, the mount point is the non varying
part of the pattern on LHS. Ie., will correctly be seen as root of web
server for all three.

Giles Thomas

unread,
Oct 28, 2008, 8:38:37 AM10/28/08
to mod...@googlegroups.com
Graham,

Many thanks for this and for all the details in your other emails - this solution works perfectly!


Cheers,

Giles
Reply all
Reply to author
Forward
0 new messages