Skip to content

Commit

Permalink
[3.0.x] Fixed #31061 -- Ignored positional args in django.urls.resolv…
Browse files Browse the repository at this point in the history
…e() when all optional named parameters are missing.

Regression in 76b993a.

Thanks Claude Paroz for the report and Carlton Gibson for reviews.
Backport of 82a88d2 from master
  • Loading branch information
felixxm committed Dec 6, 2019
1 parent 6ede5a3 commit e986e49
Show file tree
Hide file tree
Showing 5 changed files with 22 additions and 2 deletions.
3 changes: 2 additions & 1 deletion django/urls/resolvers.py
Expand Up @@ -158,8 +158,9 @@ def match(self, path):
# If there are any named groups, use those as kwargs, ignoring
# non-named groups. Otherwise, pass all non-named arguments as
# positional arguments.
kwargs = {k: v for k, v in match.groupdict().items() if v is not None}
kwargs = match.groupdict()
args = () if kwargs else match.groups()
kwargs = {k: v for k, v in kwargs.items() if v is not None}
return path[match.end():], args, kwargs
return None

Expand Down
4 changes: 4 additions & 0 deletions docs/releases/3.0.1.txt
Expand Up @@ -13,3 +13,7 @@ Bugfixes
inside Jupyter and other environments that force an async context, by adding
and option to disable :ref:`async-safety` mechanism with
``DJANGO_ALLOW_ASYNC_UNSAFE`` environment variable (:ticket:`31056`).

* Fixed a regression in Django 3.0 where ``RegexPattern``, used by
:func:`~django.urls.re_path`, returned positional arguments to be passed to
the view when all optional named groups were missing (:ticket:`31061`).
2 changes: 1 addition & 1 deletion docs/topics/http/urls.txt
Expand Up @@ -53,7 +53,7 @@ algorithm the system follows to determine which Python code to execute:
arguments:

* An instance of :class:`~django.http.HttpRequest`.
* If the matched URL pattern returned no named groups, then the
* If the matched URL pattern contained no named groups, then the
matches from the regular expression are provided as positional arguments.
* The keyword arguments are made up of any named parts matched by the
path expression, overridden by any arguments specified in the optional
Expand Down
5 changes: 5 additions & 0 deletions tests/urlpatterns/path_urls.py
Expand Up @@ -12,6 +12,11 @@
path('included_urls/', include('urlpatterns.included_urls')),
re_path(r'^regex/(?P<pk>[0-9]+)/$', views.empty_view, name='regex'),
re_path(r'^regex_optional/(?P<arg1>\d+)/(?:(?P<arg2>\d+)/)?', views.empty_view, name='regex_optional'),
re_path(
r'^regex_only_optional/(?:(?P<arg1>\d+)/)?',
views.empty_view,
name='regex_only_optional',
),
path('', include('urlpatterns.more_urls')),
path('<lang>/<path:url>/', views.empty_view, name='lang-and-path'),
]
10 changes: 10 additions & 0 deletions tests/urlpatterns/tests.py
Expand Up @@ -68,6 +68,16 @@ def test_re_path_with_optional_parameter(self):
r'^regex_optional/(?P<arg1>\d+)/(?:(?P<arg2>\d+)/)?',
)

def test_re_path_with_missing_optional_parameter(self):
match = resolve('/regex_only_optional/')
self.assertEqual(match.url_name, 'regex_only_optional')
self.assertEqual(match.kwargs, {})
self.assertEqual(match.args, ())
self.assertEqual(
match.route,
r'^regex_only_optional/(?:(?P<arg1>\d+)/)?',
)

def test_path_lookup_with_inclusion(self):
match = resolve('/included_urls/extra/something/')
self.assertEqual(match.url_name, 'inner-extra')
Expand Down

0 comments on commit e986e49

Please sign in to comment.