Ticket #16774: overlapping_urls.diff

File overlapping_urls.diff, 15.0 KB (added by meric, 12 years ago)

Patch for overlapping urls

  • django/core/handlers/base.py

    diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
    index 791382b..07484ef 100644
    a b class BaseHandler(object):  
    7373        # as a flag for initialization being complete.
    7474        self._request_middleware = request_middleware
    7575
     76    def _get_response(self, request, urlconf, resolver, url_patterns=None):
     77        "Returns an HttpResponse object for the given HttpRequest"
     78        from django.core import exceptions, urlresolvers
     79        from django.conf import settings
     80
     81        try:
     82            response = None
     83            # Apply request middleware
     84            for middleware_method in self._request_middleware:
     85                response = middleware_method(request)
     86                if response:
     87                    break
     88
     89            if response is None:
     90                if hasattr(request, "urlconf"):
     91                    # Reset url resolver with a custom urlconf.
     92                    urlconf = request.urlconf
     93                    urlresolvers.set_urlconf(urlconf)
     94                    resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
     95
     96                callback, callback_args, callback_kwargs = resolver.resolve(
     97                        request.path_info, url_patterns=url_patterns)
     98
     99                # Apply view middleware
     100                for middleware_method in self._view_middleware:
     101                    response = middleware_method(request, callback, callback_args, callback_kwargs)
     102                    if response:
     103                        break
     104
     105            if response is None:
     106                try:
     107                    response = callback(request, *callback_args, **callback_kwargs)
     108                except urlresolvers.DoesNotResolve, e:
     109                    return self._get_response(request, urlconf, resolver, url_patterns=url_patterns)
     110                except Exception as e:
     111                    # If the view raised an exception, run it through exception
     112                    # middleware, and if the exception middleware returns a
     113                    # response, use that. Otherwise, reraise the exception.
     114                    for middleware_method in self._exception_middleware:
     115                        response = middleware_method(request, e)
     116                        if response:
     117                            break
     118                    if response is None:
     119                        raise
     120
     121            # Complain if the view returned None (a common error).
     122            if response is None:
     123                if isinstance(callback, types.FunctionType):    # FBV
     124                    view_name = callback.__name__
     125                else:                                           # CBV
     126                    view_name = callback.__class__.__name__ + '.__call__'
     127                raise ValueError("The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name))
     128
     129            # If the response supports deferred rendering, apply template
     130            # response middleware and the render the response
     131            if hasattr(response, 'render') and callable(response.render):
     132                for middleware_method in self._template_response_middleware:
     133                    response = middleware_method(request, response)
     134                response = response.render()
     135
     136        except http.Http404 as e:
     137            logger.warning('Not Found: %s', request.path,
     138                        extra={
     139                            'status_code': 404,
     140                            'request': request
     141                        })
     142            if settings.DEBUG:
     143                from django.views import debug
     144                response = debug.technical_404_response(request, e)
     145            else:
     146                try:
     147                    callback, param_dict = resolver.resolve404()
     148                    response = callback(request, **param_dict)
     149                except:
     150                    signals.got_request_exception.send(sender=self.__class__, request=request)
     151                    response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
     152        except exceptions.PermissionDenied:
     153            logger.warning(
     154                'Forbidden (Permission denied): %s', request.path,
     155                extra={
     156                    'status_code': 403,
     157                    'request': request
     158                })
     159            try:
     160                callback, param_dict = resolver.resolve403()
     161                response = callback(request, **param_dict)
     162            except:
     163                signals.got_request_exception.send(
     164                        sender=self.__class__, request=request)
     165                response = self.handle_uncaught_exception(request,
     166                        resolver, sys.exc_info())
     167        except SystemExit:
     168            # Allow sys.exit() to actually exit. See tickets #1023 and #4701
     169            raise
     170        except: # Handle everything else, including SuspiciousOperation, etc.
     171            # Get the exception info now, in case another exception is thrown later.
     172            signals.got_request_exception.send(sender=self.__class__, request=request)
     173            response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
     174        return response
     175
    76176    def get_response(self, request):
    77177        "Returns an HttpResponse object for the given HttpRequest"
    78178        from django.core import exceptions, urlresolvers
    class BaseHandler(object):  
    86186            urlconf = settings.ROOT_URLCONF
    87187            urlresolvers.set_urlconf(urlconf)
    88188            resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
    89             try:
    90                 response = None
    91                 # Apply request middleware
    92                 for middleware_method in self._request_middleware:
    93                     response = middleware_method(request)
    94                     if response:
    95                         break
    96 
    97                 if response is None:
    98                     if hasattr(request, "urlconf"):
    99                         # Reset url resolver with a custom urlconf.
    100                         urlconf = request.urlconf
    101                         urlresolvers.set_urlconf(urlconf)
    102                         resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
     189            url_patterns = (pattern for pattern in resolver.url_patterns)
     190            response = self._get_response(request, urlconf, resolver, url_patterns)
    103191
    104                     callback, callback_args, callback_kwargs = resolver.resolve(
    105                             request.path_info)
    106 
    107                     # Apply view middleware
    108                     for middleware_method in self._view_middleware:
    109                         response = middleware_method(request, callback, callback_args, callback_kwargs)
    110                         if response:
    111                             break
    112 
    113                 if response is None:
    114                     try:
    115                         response = callback(request, *callback_args, **callback_kwargs)
    116                     except Exception as e:
    117                         # If the view raised an exception, run it through exception
    118                         # middleware, and if the exception middleware returns a
    119                         # response, use that. Otherwise, reraise the exception.
    120                         for middleware_method in self._exception_middleware:
    121                             response = middleware_method(request, e)
    122                             if response:
    123                                 break
    124                         if response is None:
    125                             raise
    126 
    127                 # Complain if the view returned None (a common error).
    128                 if response is None:
    129                     if isinstance(callback, types.FunctionType):    # FBV
    130                         view_name = callback.__name__
    131                     else:                                           # CBV
    132                         view_name = callback.__class__.__name__ + '.__call__'
    133                     raise ValueError("The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name))
    134 
    135                 # If the response supports deferred rendering, apply template
    136                 # response middleware and the render the response
    137                 if hasattr(response, 'render') and callable(response.render):
    138                     for middleware_method in self._template_response_middleware:
    139                         response = middleware_method(request, response)
    140                     response = response.render()
    141 
    142             except http.Http404 as e:
    143                 logger.warning('Not Found: %s', request.path,
    144                             extra={
    145                                 'status_code': 404,
    146                                 'request': request
    147                             })
    148                 if settings.DEBUG:
    149                     from django.views import debug
    150                     response = debug.technical_404_response(request, e)
    151                 else:
    152                     try:
    153                         callback, param_dict = resolver.resolve404()
    154                         response = callback(request, **param_dict)
    155                     except:
    156                         signals.got_request_exception.send(sender=self.__class__, request=request)
    157                         response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
    158             except exceptions.PermissionDenied:
    159                 logger.warning(
    160                     'Forbidden (Permission denied): %s', request.path,
    161                     extra={
    162                         'status_code': 403,
    163                         'request': request
    164                     })
    165                 try:
    166                     callback, param_dict = resolver.resolve403()
    167                     response = callback(request, **param_dict)
    168                 except:
    169                     signals.got_request_exception.send(
    170                             sender=self.__class__, request=request)
    171                     response = self.handle_uncaught_exception(request,
    172                             resolver, sys.exc_info())
    173             except SystemExit:
    174                 # Allow sys.exit() to actually exit. See tickets #1023 and #4701
    175                 raise
    176             except: # Handle everything else, including SuspiciousOperation, etc.
    177                 # Get the exception info now, in case another exception is thrown later.
    178                 signals.got_request_exception.send(sender=self.__class__, request=request)
    179                 response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
    180192        finally:
    181193            # Reset URLconf for this thread on the way out for complete
    182194            # isolation of request.urlconf
  • django/core/urlresolvers.py

    diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
    index af3df83..f697eab 100644
    a b class ResolverMatch(object):  
    7373class Resolver404(Http404):
    7474    pass
    7575
     76class DoesNotResolve(Http404):
     77    pass
     78
    7679class NoReverseMatch(Exception):
    7780    # Don't make this raise an error when used in a template.
    7881    silent_variable_failure = True
    class RegexURLResolver(LocaleRegexProvider):  
    310313            self._populate()
    311314        return self._app_dict[language_code]
    312315
    313     def resolve(self, path):
     316    def resolve(self, path, url_patterns=None):
    314317        tried = []
    315318        match = self.regex.search(path)
    316319        if match:
    317320            new_path = path[match.end():]
    318             for pattern in self.url_patterns:
     321           
     322            if url_patterns == None:
     323                url_patterns = self.url_patterns
     324
     325            for pattern in url_patterns:
    319326                try:
    320327                    sub_match = pattern.resolve(new_path)
    321328                except Resolver404 as e:
  • tests/regressiontests/views/generic_urls.py

    diff --git a/tests/regressiontests/views/generic_urls.py b/tests/regressiontests/views/generic_urls.py
    index 0f214d1..b163287 100644
    a b urlpatterns += patterns('regressiontests.views.views',  
    5454    (r'^shortcuts/render/status/$', 'render_view_with_status'),
    5555    (r'^shortcuts/render/current_app/$', 'render_view_with_current_app'),
    5656    (r'^shortcuts/render/current_app_conflict/$', 'render_view_with_current_app_conflict'),
     57    url(r'^overlapping_view/(?P<title>[a-z]+)/$', 'overlapping_view1', name='overlapping_view1'),
     58    url(r'^overlapping_view/(?P<author>[a-z]+)/$', 'overlapping_view2', name='overlapping_view2'),
     59    url(r'^overlapping_view/(?P<keywords>[a-z]+)/$', 'overlapping_view3', name='overlapping_view3'),
     60    url(r'^no_overlapping_view/(?P<keywords>[a-z]+)/$', 'no_overlapping_view', name='no_overlapping_view'),
    5761)
  • tests/regressiontests/views/tests/specials.py

    diff --git a/tests/regressiontests/views/tests/specials.py b/tests/regressiontests/views/tests/specials.py
    index 7d65284..d0fbbe9 100644
    a b  
    22from __future__ import unicode_literals
    33
    44from django.test import TestCase
     5from django.core import urlresolvers
    56
    67
    78class URLHandling(TestCase):
    class URLHandling(TestCase):  
    3738        response = self.client.get('/permanent_nonascii_redirect/')
    3839        self.assertRedirects(response, self.redirect_target, status_code=301)
    3940
     41    def test_overlapping_urls_reverse(self):
     42        url = urlresolvers.reverse('overlapping_view1', kwargs={'title':'sometitle'})
     43        self.assertEqual(url, '/overlapping_view/sometitle/')
     44        url = urlresolvers.reverse('overlapping_view2', kwargs={'author':'someauthor'})
     45        self.assertEqual(url, '/overlapping_view/someauthor/')
     46
     47    def test_overlapping_urls_resolve(self):
     48        response = self.client.get('/overlapping_view/sometitle/')
     49        self.assertContains(response, 'overlapping_view2')
     50
     51    def test_overlapping_urls_not_resolve(self):
     52        response = self.client.get('/no_overlapping_view/sometitle/')
     53        self.assertEqual(response.status_code, 404)
     54
  • tests/regressiontests/views/views.py

    diff --git a/tests/regressiontests/views/views.py b/tests/regressiontests/views/views.py
    index 2836d1b..8bc0688 100644
    a b from django.views.debug import technical_500_response, SafeExceptionReporterFilt  
    1111from django.views.decorators.debug import (sensitive_post_parameters,
    1212                                           sensitive_variables)
    1313from django.utils.log import getLogger
     14from django.core import urlresolvers
    1415
    1516from . import BrokenException, except_args
    1617
    class Klass(object):  
    227228            return technical_500_response(request, *exc_info)
    228229
    229230def sensitive_method_view(request):
    230     return Klass().method(request)
    231  No newline at end of file
     231    return Klass().method(request)
     232
     233def overlapping_view1(request, title=None):
     234    raise urlresolvers.DoesNotResolve
     235
     236def overlapping_view2(request, author=None):
     237    return HttpResponse("overlapping_view2")
     238
     239def overlapping_view3(request, keywords=None):
     240    return HttpResponse("overlapping_view3")
     241
     242def no_overlapping_view(request, keywords=None):
     243    raise urlresolvers.DoesNotResolve
Back to Top