Django

Code

Changeset 9087

Show
Ignore:
Timestamp:
09/27/08 01:14:11 (2 months ago)
Author:
mtredinnick
Message:

Fixed #9038 -- Correctly handle URL patterns with the same name (or view name),
declared independently and that differ only by argument signatures.

Patch from Russell Keith-Magee.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/core/urlresolvers.py

    r8777 r9087  
    1212from django.http import Http404 
    1313from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist 
     14from django.utils.datastructures import MultiValueDict 
    1415from django.utils.encoding import iri_to_uri, force_unicode, smart_str 
    1516from django.utils.functional import memoize 
     
    145146        self.callback = None 
    146147        self.default_kwargs = default_kwargs or {} 
    147         self._reverse_dict = {} 
     148        self._reverse_dict = MultiValueDict() 
    148149 
    149150    def __repr__(self): 
     
    163164                            new_matches.extend([(piece + suffix, p_args + args) 
    164165                                    for (suffix, args) in matches]) 
    165                         self._reverse_dict[name] = new_matches, p_pattern + pat 
     166                        self._reverse_dict.appendlist(name, (new_matches, p_pattern + pat)) 
    166167                else: 
    167168                    bits = normalize(p_pattern) 
    168                     self._reverse_dict[pattern.callback] = bits, p_pattern 
    169                     self._reverse_dict[pattern.name] = bits, p_pattern 
     169                    self._reverse_dict.appendlist(pattern.callback, (bits, p_pattern)) 
     170                    self._reverse_dict.appendlist(pattern.name, (bits, p_pattern)) 
    170171        return self._reverse_dict 
    171172    reverse_dict = property(_get_reverse_dict) 
     
    224225        except (ImportError, AttributeError), e: 
    225226            raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e)) 
    226         possibilities, pattern = self.reverse_dict.get(lookup_view, [(), ()]) 
    227         for result, params in possibilities: 
    228             if args: 
    229                 if len(args) != len(params): 
    230                     continue 
    231                 unicode_args = [force_unicode(val) for val in args] 
    232                 candidate =  result % dict(zip(params, unicode_args)) 
    233             else: 
    234                 if set(kwargs.keys()) != set(params): 
    235                     continue 
    236                 unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()]) 
    237                 candidate = result % unicode_kwargs 
    238             if re.search(u'^%s' % pattern, candidate, re.UNICODE): 
    239                 return candidate 
     227        possibilities = self.reverse_dict.getlist(lookup_view) 
     228        for possibility, pattern in possibilities: 
     229            for result, params in possibility: 
     230                if args: 
     231                    if len(args) != len(params): 
     232                        continue 
     233                    unicode_args = [force_unicode(val) for val in args] 
     234                    candidate =  result % dict(zip(params, unicode_args)) 
     235                else: 
     236                    if set(kwargs.keys()) != set(params): 
     237                        continue 
     238                    unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()]) 
     239                    candidate = result % unicode_kwargs 
     240                if re.search(u'^%s' % pattern, candidate, re.UNICODE): 
     241                    return candidate 
    240242        raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword " 
    241243                "arguments '%s' not found." % (lookup_view, args, kwargs)) 
  • django/trunk/tests/regressiontests/urlpatterns_reverse/tests.py

    r8825 r9087  
    6666    ('extra-people', '/e-people/fred/', ['fred'], {}), 
    6767    ('extra-people', '/e-people/fred/', [], {'name': 'fred'}), 
     68 
     69    # Regression for #9038 
     70    # These views are resolved by method name. Each method is deployed twice - 
     71    # once with an explicit argument, and once using the default value on 
     72    # the method. This is potentially ambiguous, as you have to pick the 
     73    # correct view for the arguments provided. 
     74    ('kwargs_view', '/arg_view/', [], {}), 
     75    ('kwargs_view', '/arg_view/10/', [], {'arg1':10}), 
     76    ('regressiontests.urlpatterns_reverse.views.absolute_kwargs_view', '/absolute_arg_view/', [], {}), 
     77    ('regressiontests.urlpatterns_reverse.views.absolute_kwargs_view', '/absolute_arg_view/10/', [], {'arg1':10}), 
     78 
    6879) 
    6980 
  • django/trunk/tests/regressiontests/urlpatterns_reverse/urls.py

    r8825 r9087  
    11from django.conf.urls.defaults import * 
    2 from views import empty_view 
     2from views import empty_view, absolute_kwargs_view 
    33 
    44urlpatterns = patterns('', 
     
    4646    # This is non-reversible, but we shouldn't blow up when parsing it. 
    4747    url(r'^(?:foo|bar)(\w+)/$', empty_view, name="disjunction"), 
     48 
     49    # Regression views for #9038. See tests for more details 
     50    url(r'arg_view/$', 'kwargs_view'), 
     51    url(r'arg_view/(?P<arg1>\d+)/$', 'kwargs_view'), 
     52    url(r'absolute_arg_view/(?P<arg1>\d+)/$', absolute_kwargs_view), 
     53    url(r'absolute_arg_view/$', absolute_kwargs_view), 
     54 
    4855) 
  • django/trunk/tests/regressiontests/urlpatterns_reverse/views.py

    r8760 r9087  
    11def empty_view(request, *args, **kwargs): 
    22    pass 
     3 
     4def kwargs_view(request, arg1=1, arg2=2): 
     5    pass 
     6 
     7def absolute_kwargs_view(request, arg1=1, arg2=2): 
     8    pass