Index: django/http/__init__.py
===================================================================
--- django/http/__init__.py (revision 6301)
+++ django/http/__init__.py (working copy)
@@ -26,6 +26,7 @@
def __init__(self):
self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {}
self.path = ''
+ self.full_path = ''
self.method = None
def __repr__(self):
@@ -71,7 +72,7 @@
location = self.get_full_path()
if not ':' in location:
current_uri = '%s://%s%s' % (self.is_secure() and 'https' or 'http',
- self.get_host(), self.path)
+ self.get_host(), self.full_path)
location = urljoin(current_uri, location)
return location
Index: django/core/handlers/wsgi.py
===================================================================
--- django/core/handlers/wsgi.py (revision 6301)
+++ django/core/handlers/wsgi.py (working copy)
@@ -75,8 +75,12 @@
class WSGIRequest(http.HttpRequest):
def __init__(self, environ):
self.environ = environ
- self.path = force_unicode(environ['PATH_INFO'])
+ self.path = force_unicode(environ.get('PATH_INFO', '/'))
+ self.full_path = (force_unicode(environ.get('SCRIPT_NAME', ''))
+ + force_unicode(environ.get('PATH_INFO', '/')))
self.META = environ
+ self.META['PATH_INFO'] = self.path
+ self.META['SCRIPT_NAME'] = force_unicode(environ.get('SCRIPT_NAME', ''))
self.method = environ['REQUEST_METHOD'].upper()
def __repr__(self):
@@ -102,7 +106,7 @@
(get, post, cookies, meta)
def get_full_path(self):
- return '%s%s' % (self.path, self.environ.get('QUERY_STRING', '') and ('?' + self.environ.get('QUERY_STRING', '')) or '')
+ return '%s%s' % (self.full_path, self.environ.get('QUERY_STRING', '') and ('?' + self.environ.get('QUERY_STRING', '')) or '')
def is_secure(self):
return 'HTTPS' in self.environ and self.environ['HTTPS'] == 'on'
Index: django/core/handlers/modpython.py
===================================================================
--- django/core/handlers/modpython.py (revision 6301)
+++ django/core/handlers/modpython.py (working copy)
@@ -14,7 +14,13 @@
class ModPythonRequest(http.HttpRequest):
def __init__(self, req):
self._req = req
- self.path = force_unicode(req.uri)
+ self.full_path = force_unicode(req.uri)
+ root = req.get_options().get('django.root', '')
+ self._django_root = force_unicode(root)
+ if root and req.uri.startswith(root):
+ self.path = force_unicode(req.uri[len(root):])
+ else:
+ self.path = self.full_path
def __repr__(self):
# Since this is called as part of error handling, we need to be very
@@ -39,7 +45,7 @@
(self.path, get, post, cookies, meta)
def get_full_path(self):
- return '%s%s' % (self.path, self._req.args and ('?' + self._req.args) or '')
+ return '%s%s' % (self.full_path, self._req.args and ('?' + self._req.args) or '')
def is_secure(self):
# Note: modpython 3.2.10+ has req.is_https(), but we need to support previous versions
@@ -94,7 +100,7 @@
'CONTENT_LENGTH': self._req.clength, # This may be wrong
'CONTENT_TYPE': self._req.content_type, # This may be wrong
'GATEWAY_INTERFACE': 'CGI/1.1',
- 'PATH_INFO': self._req.path_info,
+ 'PATH_INFO': self.path,
'PATH_TRANSLATED': None, # Not supported
'QUERY_STRING': self._req.args,
'REMOTE_ADDR': self._req.connection.remote_ip,
@@ -102,7 +108,7 @@
'REMOTE_IDENT': self._req.connection.remote_logname,
'REMOTE_USER': self._req.user,
'REQUEST_METHOD': self._req.method,
- 'SCRIPT_NAME': None, # Not supported
+ 'SCRIPT_NAME': self._django_root,
'SERVER_NAME': self._req.server.server_hostname,
'SERVER_PORT': self._req.server.port,
'SERVER_PROTOCOL': self._req.protocol,
Index: django/views/generic/create_update.py
===================================================================
--- django/views/generic/create_update.py (revision 6301)
+++ django/views/generic/create_update.py (working copy)
@@ -21,7 +21,7 @@
"""
if extra_context is None: extra_context = {}
if login_required and not request.user.is_authenticated():
- return redirect_to_login(request.path)
+ return redirect_to_login(request.full_path)
manipulator = model.AddManipulator(follow=follow)
if request.POST:
@@ -87,7 +87,7 @@
"""
if extra_context is None: extra_context = {}
if login_required and not request.user.is_authenticated():
- return redirect_to_login(request.path)
+ return redirect_to_login(request.full_path)
# Look up the object to be edited
lookup_kwargs = {}
@@ -163,7 +163,7 @@
"""
if extra_context is None: extra_context = {}
if login_required and not request.user.is_authenticated():
- return redirect_to_login(request.path)
+ return redirect_to_login(request.full_path)
# Look up the object to be edited
lookup_kwargs = {}
Index: django/views/debug.py
===================================================================
--- django/views/debug.py (revision 6301)
+++ django/views/debug.py (working copy)
@@ -345,7 +345,7 @@
Request URL: |
- {{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }} |
+ {{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.full_path|escape }} |
Exception Type: |
@@ -634,7 +634,7 @@
Request URL: |
- {{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.path|escape }} |
+ {{ request_protocol }}://{{ request.META.HTTP_HOST }}{{ request.full_path|escape }} |
Index: django/contrib/syndication/feeds.py
===================================================================
--- django/contrib/syndication/feeds.py (revision 6301)
+++ django/contrib/syndication/feeds.py (working copy)
@@ -25,7 +25,7 @@
def __init__(self, slug, request):
self.slug = slug
self.request = request
- self.feed_url = request.path
+ self.feed_url = request.full_path
self.title_template_name = self.title_template or ('feeds/%s_title.html' % slug)
self.description_template_name = self.description_template or ('feeds/%s_description.html' % slug)
Index: django/contrib/comments/views/userflags.py
===================================================================
--- django/contrib/comments/views/userflags.py (revision 6301)
+++ django/contrib/comments/views/userflags.py (working copy)
@@ -19,7 +19,7 @@
comment = get_object_or_404(Comment,pk=comment_id, site__id__exact=settings.SITE_ID)
if request.POST:
UserFlag.objects.flag(comment, request.user)
- return HttpResponseRedirect('%sdone/' % request.path)
+ return HttpResponseRedirect('%sdone/' % request.full_path)
return render_to_response('comments/flag_verify.html', {'comment': comment},
context_instance=RequestContext(request, extra_context, context_processors))
flag = login_required(flag)
@@ -50,7 +50,7 @@
comment.save()
m = ModeratorDeletion(None, request.user.id, comment.id, None)
m.save()
- return HttpResponseRedirect('%sdone/' % request.path)
+ return HttpResponseRedirect('%sdone/' % request.full_path)
return render_to_response('comments/delete_verify.html', {'comment': comment},
context_instance=RequestContext(request, extra_context, context_processors))
delete = login_required(delete)
Index: django/contrib/admin/views/auth.py
===================================================================
--- django/contrib/admin/views/auth.py (revision 6301)
+++ django/contrib/admin/views/auth.py (working copy)
@@ -20,7 +20,7 @@
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': 'user', 'obj': new_user}
if "_addanother" in request.POST:
request.user.message_set.create(message=msg)
- return HttpResponseRedirect(request.path)
+ return HttpResponseRedirect(request.full_path)
else:
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
return HttpResponseRedirect('../%s/' % new_user.id)
Index: django/contrib/admin/views/main.py
===================================================================
--- django/contrib/admin/views/main.py (revision 6301)
+++ django/contrib/admin/views/main.py (working copy)
@@ -276,7 +276,7 @@
(pk_value, force_unicode(new_object).replace('"', '\\"')))
elif "_addanother" in request.POST:
request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
- return HttpResponseRedirect(request.path)
+ return HttpResponseRedirect(request.full_path)
else:
request.user.message_set.create(message=msg)
return HttpResponseRedirect(post_url)
@@ -353,9 +353,9 @@
if "_continue" in request.POST:
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
if '_popup' in request.REQUEST:
- return HttpResponseRedirect(request.path + "?_popup=1")
+ return HttpResponseRedirect(request.full_path + "?_popup=1")
else:
- return HttpResponseRedirect(request.path)
+ return HttpResponseRedirect(request.full_path)
elif "_saveasnew" in request.POST:
request.user.message_set.create(message=_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': force_unicode(new_object)})
return HttpResponseRedirect("../%s/" % pk_value)
@@ -778,7 +778,7 @@
# is screwed up with the database, so display an error page.
if ERROR_FLAG in request.GET.keys():
return render_to_response('admin/invalid_setup.html', {'title': _('Database error')})
- return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1')
+ return HttpResponseRedirect(request.full_path + '?' + ERROR_FLAG + '=1')
c = template.RequestContext(request, {
'title': cl.title,
'is_popup': cl.is_popup,
Index: django/contrib/admin/views/decorators.py
===================================================================
--- django/contrib/admin/views/decorators.py (revision 6301)
+++ django/contrib/admin/views/decorators.py (working copy)
@@ -22,7 +22,7 @@
post_data = _encode_post_data({})
return render_to_response('admin/login.html', {
'title': _('Log in'),
- 'app_path': request.path,
+ 'app_path': request.full_path,
'post_data': post_data,
'error_message': error_message
}, context_instance=template.RequestContext(request))
@@ -99,7 +99,7 @@
return view_func(request, *args, **kwargs)
else:
request.session.delete_test_cookie()
- return http.HttpResponseRedirect(request.path)
+ return http.HttpResponseRedirect(request.full_path)
else:
return _display_login_form(request, ERROR_MESSAGE)
Index: django/contrib/admin/views/doc.py
===================================================================
--- django/contrib/admin/views/doc.py (revision 6301)
+++ django/contrib/admin/views/doc.py (working copy)
@@ -27,7 +27,7 @@
def bookmarklets(request):
# Hack! This couples this view to the URL it lives at.
- admin_root = request.path[:-len('doc/bookmarklets/')]
+ admin_root = request.full_path[:-len('doc/bookmarklets/')]
return render_to_response('admin_doc/bookmarklets.html', {
'admin_url': "%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root),
}, context_instance=RequestContext(request))
Index: django/contrib/databrowse/plugins/objects.py
===================================================================
--- django/contrib/databrowse/plugins/objects.py (revision 6301)
+++ django/contrib/databrowse/plugins/objects.py (working copy)
@@ -8,7 +8,7 @@
def model_view(self, request, model_databrowse, url):
# If the object ID wasn't provided, redirect to the model page, which is one level up.
if url is None:
- return http.HttpResponseRedirect(urlparse.urljoin(request.path, '../'))
+ return http.HttpResponseRedirect(urlparse.urljoin(request.full_path, '../'))
easy_model = EasyModel(model_databrowse.site, model_databrowse.model)
obj = easy_model.object_by_pk(url)
return render_to_response('databrowse/object_detail.html', {'object': obj, 'root_url': model_databrowse.site.root_url})
Index: django/contrib/databrowse/sites.py
===================================================================
--- django/contrib/databrowse/sites.py (revision 6301)
+++ django/contrib/databrowse/sites.py (working copy)
@@ -110,7 +110,7 @@
`url` is the remainder of the URL -- e.g. 'comments/comment/'.
"""
- self.root_url = request.path[:len(request.path) - len(url)]
+ self.root_url = request.full_path[:len(request.full_path) - len(url)]
url = url.rstrip('/') # Trim trailing slash, if it exists.
if url == '':
Index: django/contrib/auth/views.py
===================================================================
--- django/contrib/auth/views.py (revision 6301)
+++ django/contrib/auth/views.py (working copy)
@@ -47,7 +47,7 @@
return render_to_response(template_name, {'title': _('Logged out')}, context_instance=RequestContext(request))
else:
# Redirect to this page until the session has been cleared.
- return HttpResponseRedirect(next_page or request.path)
+ return HttpResponseRedirect(next_page or request.full_path)
def logout_then_login(request, login_url=None):
"Logs out the user if he is logged in. Then redirects to the log-in page."
@@ -75,7 +75,7 @@
form.save(domain_override=request.META['HTTP_HOST'])
else:
form.save(email_template_name=email_template_name)
- return HttpResponseRedirect('%sdone/' % request.path)
+ return HttpResponseRedirect('%sdone/' % request.full_path)
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
context_instance=RequestContext(request))
@@ -90,7 +90,7 @@
errors = form.get_validation_errors(new_data)
if not errors:
form.save(new_data)
- return HttpResponseRedirect('%sdone/' % request.path)
+ return HttpResponseRedirect('%sdone/' % request.full_path)
return render_to_response(template_name, {'form': oldforms.FormWrapper(form, new_data, errors)},
context_instance=RequestContext(request))
password_change = login_required(password_change)
Index: django/contrib/flatpages/views.py
===================================================================
--- django/contrib/flatpages/views.py (revision 6301)
+++ django/contrib/flatpages/views.py (working copy)
@@ -25,7 +25,7 @@
# logged in, redirect to the login page.
if f.registration_required and not request.user.is_authenticated():
from django.contrib.auth.views import redirect_to_login
- return redirect_to_login(request.path)
+ return redirect_to_login(request.full_path)
if f.template_name:
t = loader.select_template((f.template_name, DEFAULT_TEMPLATE))
else:
Index: django/middleware/common.py
===================================================================
--- django/middleware/common.py (revision 6301)
+++ django/middleware/common.py (working copy)
@@ -33,7 +33,7 @@
# Check for a redirect based on settings.APPEND_SLASH and settings.PREPEND_WWW
host = request.get_host()
- old_url = [host, request.path]
+ old_url = [host, request.full_path]
new_url = old_url[:]
if settings.PREPEND_WWW and old_url[0] and not old_url[0].startswith('www.'):
new_url[0] = 'www.' + old_url[0]
Index: AUTHORS
===================================================================
--- AUTHORS (revision 6301)
+++ AUTHORS (working copy)
@@ -319,6 +319,7 @@
ymasuda@ethercube.com
Jarek Zgoda
Cheng Zhang
+ John Melesky
A big THANK YOU goes to:
Index: docs/request_response.txt
===================================================================
--- docs/request_response.txt (revision 6301)
+++ docs/request_response.txt (working copy)
@@ -24,6 +24,15 @@
All attributes except ``session`` should be considered read-only.
``path``
+ A string representing the path to the requested page relative to
+ the Django root, not including the domain. For WSGI servers, this
+ means it does not include SCRIPT_NAME. For mod_python servers,
+ this does not include the user-configurable django.root
+ PythonOption.
+
+ Example: ``"/bands/the_beatles/"``
+
+``full_path``
A string representing the full path to the requested page, not including
the domain.
@@ -112,6 +121,7 @@
* ``REQUEST_METHOD`` -- A string such as ``"GET"`` or ``"POST"``.
* ``SERVER_NAME`` -- The hostname of the server.
* ``SERVER_PORT`` -- The port of the server.
+ * ``SCRIPT_NAME`` -- The location of the Django app (e.g. "/mysite")
``user``
A ``django.contrib.auth.models.User`` object representing the currently
@@ -157,7 +167,7 @@
``request.POST`` has the given key.
``get_full_path()``
- Returns the ``path``, plus an appended query string, if applicable.
+ Returns the ``full_path``, plus an appended query string, if applicable.
Example: ``"/music/bands/the_beatles/?print=true"``
Index: docs/modpython.txt
===================================================================
--- docs/modpython.txt (revision 6301)
+++ docs/modpython.txt (working copy)
@@ -36,6 +36,7 @@
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
PythonDebug On
+ PythonOption django.root /mysite
...and replace ``mysite.settings`` with the Python import path to your Django
@@ -45,6 +46,11 @@
Django mod_python handler." It passes the value of ``DJANGO_SETTINGS_MODULE``
so mod_python knows which settings to use.
+The PythonOption tells Django: "Pass all requests through to the application as
+if they were rooted at '/', but generate absolute URLs under '/mysite'." This
+way, Django applications can be installed in any subdirectory URL without
+having the full path hardwired in.
+
Note that we're using the ```` directive, not the ````
directive. The latter is used for pointing at places on your filesystem,
whereas ```` points at places in the URL structure of a Web site.
@@ -60,6 +66,7 @@
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE mysite.settings
PythonDebug On
+ PythonOption django.root /mysite
**PythonPath "['/path/to/project'] + sys.path"**