Added first stab at reverse matching to urlresolvers.py
git-svn-id: http://code.djangoproject.com/svn/django/trunk@2910 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
3cef18cb51
commit
d9c4af6b37
|
@ -14,12 +14,56 @@ import re
|
|||
class Resolver404(Http404):
|
||||
pass
|
||||
|
||||
class NoReverseMatch(Exception):
|
||||
pass
|
||||
|
||||
def get_mod_func(callback):
|
||||
# Converts 'django.views.news.stories.story_detail' to
|
||||
# ['django.views.news.stories', 'story_detail']
|
||||
dot = callback.rindex('.')
|
||||
return callback[:dot], callback[dot+1:]
|
||||
|
||||
class MatchChecker(object):
|
||||
"Class used in reverse RegexURLPattern lookup."
|
||||
def __init__(self, args, kwargs):
|
||||
self.args, self.kwargs = args, kwargs
|
||||
self.current_arg = 0
|
||||
|
||||
def __call__(self, match_obj):
|
||||
# match_obj.group(1) is the contents of the parenthesis.
|
||||
# First we need to figure out whether it's a named or unnamed group.
|
||||
#
|
||||
grouped = match_obj.group(1)
|
||||
m = re.search(r'^\?P<(\w+)>(.*?)$', grouped)
|
||||
if m: # If this was a named group...
|
||||
# m.group(1) is the name of the group
|
||||
# m.group(2) is the regex.
|
||||
try:
|
||||
value = self.kwargs[m.group(1)]
|
||||
except KeyError:
|
||||
# It was a named group, but the arg was passed in as a
|
||||
# positional arg or not at all.
|
||||
try:
|
||||
value = self.args[self.current_arg]
|
||||
self.current_arg += 1
|
||||
except IndexError:
|
||||
# The arg wasn't passed in.
|
||||
raise NoReverseMatch('Not enough positional arguments passed in')
|
||||
test_regex = m.group(2)
|
||||
else: # Otherwise, this was a positional (unnamed) group.
|
||||
try:
|
||||
value = self.args[self.current_arg]
|
||||
self.current_arg += 1
|
||||
except IndexError:
|
||||
# The arg wasn't passed in.
|
||||
raise NoReverseMatch('Not enough positional arguments passed in')
|
||||
test_regex = grouped
|
||||
# Note we're using re.match here on purpose because the start of
|
||||
# to string needs to match.
|
||||
if not re.match(test_regex + '$', str(value)): # TODO: Unicode?
|
||||
raise NoReverseMatch("Value %r didn't match regular expression %r" % (value, test_regex))
|
||||
return str(value) # TODO: Unicode?
|
||||
|
||||
class RegexURLPattern:
|
||||
def __init__(self, regex, callback, default_args=None):
|
||||
# regex is a string representing a regular expression.
|
||||
|
@ -58,12 +102,37 @@ class RegexURLPattern:
|
|||
except AttributeError, e:
|
||||
raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e))
|
||||
|
||||
def reverse(self, viewname, *args, **kwargs):
|
||||
if viewname != self.callback:
|
||||
raise NoReverseMatch
|
||||
return self.reverse_helper(*args, **kwargs)
|
||||
|
||||
def reverse_helper(self, *args, **kwargs):
|
||||
"""
|
||||
Does a "reverse" lookup -- returns the URL for the given args/kwargs.
|
||||
The args/kwargs are applied to the regular expression in this
|
||||
RegexURLPattern. For example:
|
||||
|
||||
>>> RegexURLPattern('^places/(\d+)/$').reverse_helper(3)
|
||||
'places/3/'
|
||||
>>> RegexURLPattern('^places/(?P<id>\d+)/$').reverse_helper(id=3)
|
||||
'places/3/'
|
||||
>>> RegexURLPattern('^people/(?P<state>\w\w)/(\w+)/$').reverse_helper('adrian', state='il')
|
||||
'people/il/adrian/'
|
||||
|
||||
Raises NoReverseMatch if the args/kwargs aren't valid for the RegexURLPattern.
|
||||
"""
|
||||
# TODO: Handle nested parenthesis in the following regex.
|
||||
result = re.sub(r'\(([^)]+)\)', MatchChecker(args, kwargs), self.regex.pattern)
|
||||
return result.replace('^', '').replace('$', '')
|
||||
|
||||
class RegexURLResolver(object):
|
||||
def __init__(self, regex, urlconf_name):
|
||||
# regex is a string representing a regular expression.
|
||||
# urlconf_name is a string representing the module containing urlconfs.
|
||||
self.regex = re.compile(regex)
|
||||
self.urlconf_name = urlconf_name
|
||||
self.callback = None
|
||||
|
||||
def resolve(self, path):
|
||||
tried = []
|
||||
|
@ -110,3 +179,12 @@ class RegexURLResolver(object):
|
|||
|
||||
def resolve500(self):
|
||||
return self._resolve_special('500')
|
||||
|
||||
def reverse(self, viewname, *args, **kwargs):
|
||||
for pattern in self.urlconf_module.urlpatterns:
|
||||
if pattern.callback == viewname:
|
||||
try:
|
||||
return pattern.reverse_helper(*args, **kwargs)
|
||||
except NoReverseMatch:
|
||||
continue
|
||||
raise NoReverseMatch
|
||||
|
|
Loading…
Reference in New Issue