mirror of
https://github.com/django/django.git
synced 2025-10-25 06:36:07 +00:00
Changed/fixed the way Django handles SCRIPT_NAME and PATH_INFO (or
equivalents). Basically, URL resolving will only use the PATH_INFO and the SCRIPT_NAME will be prepended by reverse() automatically. Allows for more portable development and installation. Also exposes SCRIPT_NAME in the HttpRequest instance. There are a number of cases where things don't work completely transparently, so mod_python and fastcgi users should read the relevant docs. Fixed #285, #1516, #3414. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8015 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -3,6 +3,7 @@ import sys
|
||||
from django import http
|
||||
from django.core import signals
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils.encoding import force_unicode
|
||||
|
||||
class BaseHandler(object):
|
||||
# Changes that are always applied to a response (in this order).
|
||||
@@ -73,7 +74,8 @@ class BaseHandler(object):
|
||||
|
||||
resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
|
||||
try:
|
||||
callback, callback_args, callback_kwargs = resolver.resolve(request.path)
|
||||
callback, callback_args, callback_kwargs = resolver.resolve(
|
||||
request.path_info)
|
||||
|
||||
# Apply view middleware
|
||||
for middleware_method in self._view_middleware:
|
||||
@@ -170,3 +172,27 @@ class BaseHandler(object):
|
||||
response = func(request, response)
|
||||
return response
|
||||
|
||||
def get_script_name(environ):
|
||||
"""
|
||||
Returns the equivalent of the HTTP request's SCRIPT_NAME environment
|
||||
variable. If Apache mod_rewrite has been used, returns what would have been
|
||||
the script name prior to any rewriting (so it's the script name as seen
|
||||
from the client's perspective), unless DJANGO_USE_POST_REWRITE is set (to
|
||||
anything).
|
||||
"""
|
||||
from django.conf import settings
|
||||
if settings.FORCE_SCRIPT_NAME is not None:
|
||||
return force_unicode(settings.FORCE_SCRIPT_NAME)
|
||||
|
||||
# If Apache's mod_rewrite had a whack at the URL, Apache set either
|
||||
# SCRIPT_URL or REDIRECT_URL to the full resource URL before applying any
|
||||
# rewrites. Unfortunately not every webserver (lighttpd!) passes this
|
||||
# information through all the time, so FORCE_SCRIPT_NAME, above, is still
|
||||
# needed.
|
||||
script_url = environ.get('SCRIPT_URL', u'')
|
||||
if not script_url:
|
||||
script_url = environ.get('REDIRECT_URL', u'')
|
||||
if script_url:
|
||||
return force_unicode(script_url[:-len(environ.get('PATH_INFO', ''))])
|
||||
return force_unicode(environ.get('SCRIPT_NAME', u''))
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from pprint import pformat
|
||||
from django import http
|
||||
from django.core import signals
|
||||
from django.core.handlers.base import BaseHandler
|
||||
from django.core.urlresolvers import set_script_prefix
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils import datastructures
|
||||
from django.utils.encoding import force_unicode, smart_str
|
||||
@@ -15,7 +16,21 @@ from django.utils.encoding import force_unicode, smart_str
|
||||
class ModPythonRequest(http.HttpRequest):
|
||||
def __init__(self, req):
|
||||
self._req = req
|
||||
# FIXME: This isn't ideal. The request URI may be encoded (it's
|
||||
# non-normalized) slightly differently to the "real" SCRIPT_NAME
|
||||
# and PATH_INFO values. This causes problems when we compute path_info,
|
||||
# below. For now, don't use script names that will be subject to
|
||||
# encoding/decoding.
|
||||
self.path = force_unicode(req.uri)
|
||||
root = req.get_options().get('django.root', '')
|
||||
self.django_root = root
|
||||
# req.path_info isn't necessarily computed correctly in all
|
||||
# circumstances (it's out of mod_python's control a bit), so we use
|
||||
# req.uri and some string manipulations to get the right value.
|
||||
if root and req.uri.startswith(root):
|
||||
self.path_info = force_unicode(req.uri[len(root):])
|
||||
else:
|
||||
self.path_info = self.path
|
||||
|
||||
def __repr__(self):
|
||||
# Since this is called as part of error handling, we need to be very
|
||||
@@ -100,7 +115,7 @@ class ModPythonRequest(http.HttpRequest):
|
||||
'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_info,
|
||||
'PATH_TRANSLATED': None, # Not supported
|
||||
'QUERY_STRING': self._req.args,
|
||||
'REMOTE_ADDR': self._req.connection.remote_ip,
|
||||
@@ -108,7 +123,7 @@ class ModPythonRequest(http.HttpRequest):
|
||||
'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,
|
||||
@@ -153,6 +168,7 @@ class ModPythonHandler(BaseHandler):
|
||||
if self._request_middleware is None:
|
||||
self.load_middleware()
|
||||
|
||||
set_script_prefix(req.get_options().get('django.root', ''))
|
||||
dispatcher.send(signal=signals.request_started)
|
||||
try:
|
||||
try:
|
||||
|
||||
@@ -7,7 +7,8 @@ except ImportError:
|
||||
|
||||
from django import http
|
||||
from django.core import signals
|
||||
from django.core.handlers.base import BaseHandler
|
||||
from django.core.handlers import base
|
||||
from django.core.urlresolvers import set_script_prefix
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils import datastructures
|
||||
from django.utils.encoding import force_unicode
|
||||
@@ -74,9 +75,14 @@ def safe_copyfileobj(fsrc, fdst, length=16*1024, size=0):
|
||||
|
||||
class WSGIRequest(http.HttpRequest):
|
||||
def __init__(self, environ):
|
||||
script_name = base.get_script_name(environ)
|
||||
path_info = force_unicode(environ.get('PATH_INFO', '/'))
|
||||
self.environ = environ
|
||||
self.path = force_unicode(environ['PATH_INFO'])
|
||||
self.path_info = path_info
|
||||
self.path = '%s%s' % (script_name, path_info)
|
||||
self.META = environ
|
||||
self.META['PATH_INFO'] = path_info
|
||||
self.META['SCRIPT_NAME'] = script_name
|
||||
self.method = environ['REQUEST_METHOD'].upper()
|
||||
|
||||
def __repr__(self):
|
||||
@@ -178,7 +184,7 @@ class WSGIRequest(http.HttpRequest):
|
||||
REQUEST = property(_get_request)
|
||||
raw_post_data = property(_get_raw_post_data)
|
||||
|
||||
class WSGIHandler(BaseHandler):
|
||||
class WSGIHandler(base.BaseHandler):
|
||||
initLock = Lock()
|
||||
request_class = WSGIRequest
|
||||
|
||||
@@ -194,6 +200,7 @@ class WSGIHandler(BaseHandler):
|
||||
self.load_middleware()
|
||||
self.initLock.release()
|
||||
|
||||
set_script_prefix(base.get_script_name(environ))
|
||||
dispatcher.send(signal=signals.request_started)
|
||||
try:
|
||||
try:
|
||||
|
||||
@@ -7,11 +7,13 @@ a string) and returns a tuple in this format:
|
||||
(view_function, function_args, function_kwargs)
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from django.http import Http404
|
||||
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
|
||||
from django.utils.encoding import iri_to_uri, force_unicode, smart_str
|
||||
from django.utils.functional import memoize
|
||||
import re
|
||||
from django.utils.thread_support import currentThread
|
||||
|
||||
try:
|
||||
reversed
|
||||
@@ -21,6 +23,11 @@ except NameError:
|
||||
_resolver_cache = {} # Maps urlconf modules to RegexURLResolver instances.
|
||||
_callable_cache = {} # Maps view and url pattern names to their view functions.
|
||||
|
||||
# SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
|
||||
# the current thread (which is the only one we ever access), it is assumed to
|
||||
# be empty.
|
||||
_prefixes = {}
|
||||
|
||||
class Resolver404(Http404):
|
||||
pass
|
||||
|
||||
@@ -291,13 +298,33 @@ class RegexURLResolver(object):
|
||||
def resolve(path, urlconf=None):
|
||||
return get_resolver(urlconf).resolve(path)
|
||||
|
||||
def reverse(viewname, urlconf=None, args=None, kwargs=None):
|
||||
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None):
|
||||
args = args or []
|
||||
kwargs = kwargs or {}
|
||||
return iri_to_uri(u'/' + get_resolver(urlconf).reverse(viewname, *args, **kwargs))
|
||||
if prefix is None:
|
||||
prefix = get_script_prefix()
|
||||
return iri_to_uri(u'%s%s' % (prefix, get_resolver(urlconf).reverse(viewname,
|
||||
*args, **kwargs)))
|
||||
|
||||
def clear_url_caches():
|
||||
global _resolver_cache
|
||||
global _callable_cache
|
||||
_resolver_cache.clear()
|
||||
_callable_cache.clear()
|
||||
|
||||
def set_script_prefix(prefix):
|
||||
"""
|
||||
Sets the script prefix for the current thread.
|
||||
"""
|
||||
if not prefix.endswith('/'):
|
||||
prefix += '/'
|
||||
_prefixes[currentThread()] = prefix
|
||||
|
||||
def get_script_prefix():
|
||||
"""
|
||||
Returns the currently active script prefix. Useful for client code that
|
||||
wishes to construct their own URLs manually (although accessing the request
|
||||
instance is normally going to be a lot cleaner).
|
||||
"""
|
||||
return _prefixes.get(currentThread(), u'/')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user