diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index eebe54e99b..8ca602c241 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -8,6 +8,7 @@ a string) and returns a tuple in this format: """ from __future__ import unicode_literals +import functools from importlib import import_module import re from threading import local @@ -270,6 +271,9 @@ class RegexURLResolver(LocaleRegexProvider): self._callback_strs.add(pattern._callback_str) elif hasattr(pattern, '_callback'): callback = pattern._callback + if isinstance(callback, functools.partial): + callback = callback.func + if not hasattr(callback, '__name__'): lookup_str = callback.__module__ + "." + callback.__class__.__name__ else: diff --git a/docs/releases/1.4.12.txt b/docs/releases/1.4.12.txt new file mode 100644 index 0000000000..561125a2d0 --- /dev/null +++ b/docs/releases/1.4.12.txt @@ -0,0 +1,14 @@ +=========================== +Django 1.4.12 release notes +=========================== + +*Under development* + +Django 1.4.12 fixes a regression in the 1.4.11 security release. + +Bugfixes +======== + +* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views + created using :func:`functools.partial()` + (`#22486 `_) diff --git a/docs/releases/1.5.7.txt b/docs/releases/1.5.7.txt new file mode 100644 index 0000000000..c6987e3e5b --- /dev/null +++ b/docs/releases/1.5.7.txt @@ -0,0 +1,14 @@ +========================== +Django 1.5.7 release notes +========================== + +*Under development* + +Django 1.5.7 fixes a regression in the 1.5.6 security release. + +Bugfixes +======== + +* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views + created using :func:`functools.partial()` + (`#22486 `_) diff --git a/docs/releases/1.6.4.txt b/docs/releases/1.6.4.txt index 6bf0b6fef0..fd36b21271 100644 --- a/docs/releases/1.6.4.txt +++ b/docs/releases/1.6.4.txt @@ -12,3 +12,7 @@ Bugfixes * Added backwards compatibility support for the :mod:`django.contrib.messages` cookie format of Django 1.4 and earlier to facilitate upgrading to 1.6 from 1.4 (`#22426 `_). + +* Restored the ability to :meth:`~django.core.urlresolvers.reverse` views + created using :func:`functools.partial()` + (`#22486 `_) diff --git a/docs/releases/index.txt b/docs/releases/index.txt index 6beffadca5..898888be3f 100644 --- a/docs/releases/index.txt +++ b/docs/releases/index.txt @@ -40,6 +40,7 @@ Final releases .. toctree:: :maxdepth: 1 + 1.5.7 1.5.6 1.5.5 1.5.4 @@ -53,6 +54,7 @@ Final releases .. toctree:: :maxdepth: 1 + 1.4.12 1.4.11 1.4.10 1.4.9 diff --git a/tests/urlpatterns_reverse/urls.py b/tests/urlpatterns_reverse/urls.py index c79715e5bd..f7b160f626 100644 --- a/tests/urlpatterns_reverse/urls.py +++ b/tests/urlpatterns_reverse/urls.py @@ -1,6 +1,6 @@ from django.conf.urls import patterns, url, include -from .views import empty_view, absolute_kwargs_view +from .views import empty_view, empty_view_partial, empty_view_wrapped, absolute_kwargs_view other_patterns = patterns('', @@ -51,6 +51,10 @@ urlpatterns = patterns('', # This is non-reversible, but we shouldn't blow up when parsing it. url(r'^(?:foo|bar)(\w+)/$', empty_view, name="disjunction"), + # Partials should be fine. + url(r'^partial/', empty_view_partial, name="partial"), + url(r'^partial_wrapped/', empty_view_wrapped, name="partial_wrapped"), + # Regression views for #9038. See tests for more details url(r'arg_view/$', 'kwargs_view'), url(r'arg_view/(?P\d+)/$', 'kwargs_view'), diff --git a/tests/urlpatterns_reverse/views.py b/tests/urlpatterns_reverse/views.py index 610feb8451..efd5689c31 100644 --- a/tests/urlpatterns_reverse/views.py +++ b/tests/urlpatterns_reverse/views.py @@ -1,3 +1,5 @@ +from functools import partial, update_wrapper + from django.http import HttpResponse from django.views.generic import RedirectView from django.core.urlresolvers import reverse_lazy @@ -55,3 +57,11 @@ def login_required_view(request): def bad_view(request, *args, **kwargs): raise ValueError("I don't think I'm getting good value for this view") + + +empty_view_partial = partial(empty_view, template_name="template.html") + + +empty_view_wrapped = update_wrapper( + partial(empty_view, template_name="template.html"), empty_view, +)