mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Fixed #16406 -- Added ResolveMatch.captured_kwargs and extra_kwargs.
Thanks Florian Apolloner for the review and implementation idea.
This commit is contained in:
parent
83c803f161
commit
baf9604ed8
@ -41,6 +41,8 @@ class ResolverMatch:
|
|||||||
namespaces=None,
|
namespaces=None,
|
||||||
route=None,
|
route=None,
|
||||||
tried=None,
|
tried=None,
|
||||||
|
captured_kwargs=None,
|
||||||
|
extra_kwargs=None,
|
||||||
):
|
):
|
||||||
self.func = func
|
self.func = func
|
||||||
self.args = args
|
self.args = args
|
||||||
@ -48,6 +50,8 @@ class ResolverMatch:
|
|||||||
self.url_name = url_name
|
self.url_name = url_name
|
||||||
self.route = route
|
self.route = route
|
||||||
self.tried = tried
|
self.tried = tried
|
||||||
|
self.captured_kwargs = captured_kwargs
|
||||||
|
self.extra_kwargs = extra_kwargs
|
||||||
|
|
||||||
# If a URLRegexResolver doesn't have a namespace or app_name, it passes
|
# If a URLRegexResolver doesn't have a namespace or app_name, it passes
|
||||||
# in an empty value.
|
# in an empty value.
|
||||||
@ -78,7 +82,7 @@ class ResolverMatch:
|
|||||||
func = self._func_path
|
func = self._func_path
|
||||||
return (
|
return (
|
||||||
"ResolverMatch(func=%s, args=%r, kwargs=%r, url_name=%r, "
|
"ResolverMatch(func=%s, args=%r, kwargs=%r, url_name=%r, "
|
||||||
"app_names=%r, namespaces=%r, route=%r)"
|
"app_names=%r, namespaces=%r, route=%r%s%s)"
|
||||||
% (
|
% (
|
||||||
func,
|
func,
|
||||||
self.args,
|
self.args,
|
||||||
@ -87,6 +91,10 @@ class ResolverMatch:
|
|||||||
self.app_names,
|
self.app_names,
|
||||||
self.namespaces,
|
self.namespaces,
|
||||||
self.route,
|
self.route,
|
||||||
|
f", captured_kwargs={self.captured_kwargs!r}"
|
||||||
|
if self.captured_kwargs
|
||||||
|
else "",
|
||||||
|
f", extra_kwargs={self.extra_kwargs!r}" if self.extra_kwargs else "",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -416,11 +424,17 @@ class URLPattern:
|
|||||||
def resolve(self, path):
|
def resolve(self, path):
|
||||||
match = self.pattern.match(path)
|
match = self.pattern.match(path)
|
||||||
if match:
|
if match:
|
||||||
new_path, args, kwargs = match
|
new_path, args, captured_kwargs = match
|
||||||
# Pass any extra_kwargs as **kwargs.
|
# Pass any default args as **kwargs.
|
||||||
kwargs.update(self.default_args)
|
kwargs = {**captured_kwargs, **self.default_args}
|
||||||
return ResolverMatch(
|
return ResolverMatch(
|
||||||
self.callback, args, kwargs, self.pattern.name, route=str(self.pattern)
|
self.callback,
|
||||||
|
args,
|
||||||
|
kwargs,
|
||||||
|
self.pattern.name,
|
||||||
|
route=str(self.pattern),
|
||||||
|
captured_kwargs=captured_kwargs,
|
||||||
|
extra_kwargs=self.default_args,
|
||||||
)
|
)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
@ -678,6 +692,11 @@ class URLResolver:
|
|||||||
[self.namespace] + sub_match.namespaces,
|
[self.namespace] + sub_match.namespaces,
|
||||||
self._join_route(current_route, sub_match.route),
|
self._join_route(current_route, sub_match.route),
|
||||||
tried,
|
tried,
|
||||||
|
captured_kwargs=sub_match.captured_kwargs,
|
||||||
|
extra_kwargs={
|
||||||
|
**self.default_kwargs,
|
||||||
|
**sub_match.extra_kwargs,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
tried.append([pattern])
|
tried.append([pattern])
|
||||||
raise Resolver404({"tried": tried, "path": new_path})
|
raise Resolver404({"tried": tried, "path": new_path})
|
||||||
@ -737,7 +756,14 @@ class URLResolver:
|
|||||||
else:
|
else:
|
||||||
if set(kwargs).symmetric_difference(params).difference(defaults):
|
if set(kwargs).symmetric_difference(params).difference(defaults):
|
||||||
continue
|
continue
|
||||||
if any(kwargs.get(k, v) != v for k, v in defaults.items()):
|
matches = True
|
||||||
|
for k, v in defaults.items():
|
||||||
|
if k in params:
|
||||||
|
continue
|
||||||
|
if kwargs.get(k, v) != v:
|
||||||
|
matches = False
|
||||||
|
break
|
||||||
|
if not matches:
|
||||||
continue
|
continue
|
||||||
candidate_subs = kwargs
|
candidate_subs = kwargs
|
||||||
# Convert the candidate subs to text using Converter.to_url().
|
# Convert the candidate subs to text using Converter.to_url().
|
||||||
|
@ -123,9 +123,24 @@ If the URL does not resolve, the function raises a
|
|||||||
|
|
||||||
.. attribute:: ResolverMatch.kwargs
|
.. attribute:: ResolverMatch.kwargs
|
||||||
|
|
||||||
The keyword arguments that would be passed to the view
|
All keyword arguments that would be passed to the view function, i.e.
|
||||||
|
:attr:`~ResolverMatch.captured_kwargs` and
|
||||||
|
:attr:`~ResolverMatch.extra_kwargs`.
|
||||||
|
|
||||||
|
.. attribute:: ResolverMatch.captured_kwargs
|
||||||
|
|
||||||
|
.. versionadded:: 4.1
|
||||||
|
|
||||||
|
The captured keyword arguments that would be passed to the view
|
||||||
function, as parsed from the URL.
|
function, as parsed from the URL.
|
||||||
|
|
||||||
|
.. attribute:: ResolverMatch.extra_kwargs
|
||||||
|
|
||||||
|
.. versionadded:: 4.1
|
||||||
|
|
||||||
|
The additional keyword arguments that would be passed to the view
|
||||||
|
function.
|
||||||
|
|
||||||
.. attribute:: ResolverMatch.url_name
|
.. attribute:: ResolverMatch.url_name
|
||||||
|
|
||||||
The name of the URL pattern that matches the URL.
|
The name of the URL pattern that matches the URL.
|
||||||
|
@ -326,7 +326,11 @@ Tests
|
|||||||
URLs
|
URLs
|
||||||
~~~~
|
~~~~
|
||||||
|
|
||||||
* ...
|
* The new :attr:`.ResolverMatch.captured_kwargs` attribute stores the captured
|
||||||
|
keyword arguments, as parsed from the URL.
|
||||||
|
|
||||||
|
* The new :attr:`.ResolverMatch.extra_kwargs` attribute stores the additional
|
||||||
|
keyword arguments passed to the view function.
|
||||||
|
|
||||||
Utilities
|
Utilities
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
@ -8,7 +8,7 @@ from django.template import Context, Template
|
|||||||
from django.test import SimpleTestCase, override_settings
|
from django.test import SimpleTestCase, override_settings
|
||||||
from django.test.client import RequestFactory
|
from django.test.client import RequestFactory
|
||||||
from django.test.utils import override_script_prefix
|
from django.test.utils import override_script_prefix
|
||||||
from django.urls import clear_url_caches, reverse, translate_url
|
from django.urls import clear_url_caches, resolve, reverse, translate_url
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
|
|
||||||
|
|
||||||
@ -198,6 +198,23 @@ class URLTranslationTests(URLTestCaseBase):
|
|||||||
self.assertEqual(translate_url("/nl/gebruikers/", "en"), "/en/users/")
|
self.assertEqual(translate_url("/nl/gebruikers/", "en"), "/en/users/")
|
||||||
self.assertEqual(translation.get_language(), "nl")
|
self.assertEqual(translation.get_language(), "nl")
|
||||||
|
|
||||||
|
def test_reverse_translated_with_captured_kwargs(self):
|
||||||
|
with translation.override("en"):
|
||||||
|
match = resolve("/translated/apo/")
|
||||||
|
# Links to the same page in other languages.
|
||||||
|
tests = [
|
||||||
|
("nl", "/vertaald/apo/"),
|
||||||
|
("pt-br", "/traduzidos/apo/"),
|
||||||
|
]
|
||||||
|
for lang, expected_link in tests:
|
||||||
|
with translation.override(lang):
|
||||||
|
self.assertEqual(
|
||||||
|
reverse(
|
||||||
|
match.url_name, args=match.args, kwargs=match.captured_kwargs
|
||||||
|
),
|
||||||
|
expected_link,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class URLNamespaceTests(URLTestCaseBase):
|
class URLNamespaceTests(URLTestCaseBase):
|
||||||
"""
|
"""
|
||||||
|
@ -10,7 +10,10 @@ urlpatterns = [
|
|||||||
path("not-prefixed-include/", include("i18n.patterns.urls.included")),
|
path("not-prefixed-include/", include("i18n.patterns.urls.included")),
|
||||||
re_path(_(r"^translated/$"), view, name="no-prefix-translated"),
|
re_path(_(r"^translated/$"), view, name="no-prefix-translated"),
|
||||||
re_path(
|
re_path(
|
||||||
_(r"^translated/(?P<slug>[\w-]+)/$"), view, name="no-prefix-translated-slug"
|
_(r"^translated/(?P<slug>[\w-]+)/$"),
|
||||||
|
view,
|
||||||
|
{"slug": "default-slug"},
|
||||||
|
name="no-prefix-translated-slug",
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3,5 +3,10 @@ from django.urls import re_path
|
|||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
re_path(r"^more/(?P<extra>\w+)/$", views.empty_view, name="inner-more"),
|
re_path(
|
||||||
|
r"^more/(?P<extra>\w+)/$",
|
||||||
|
views.empty_view,
|
||||||
|
{"sub-extra": True},
|
||||||
|
name="inner-more",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
@ -13,6 +13,13 @@ urlpatterns = [
|
|||||||
views.empty_view,
|
views.empty_view,
|
||||||
name="articles-year-month-day",
|
name="articles-year-month-day",
|
||||||
),
|
),
|
||||||
|
path("books/2007/", views.empty_view, {"extra": True}, name="books-2007"),
|
||||||
|
path(
|
||||||
|
"books/<int:year>/<int:month>/<int:day>/",
|
||||||
|
views.empty_view,
|
||||||
|
{"extra": True},
|
||||||
|
name="books-year-month-day",
|
||||||
|
),
|
||||||
path("users/", views.empty_view, name="users"),
|
path("users/", views.empty_view, name="users"),
|
||||||
path("users/<id>/", views.empty_view, name="user-with-id"),
|
path("users/<id>/", views.empty_view, name="user-with-id"),
|
||||||
path("included_urls/", include("urlpatterns.included_urls")),
|
path("included_urls/", include("urlpatterns.included_urls")),
|
||||||
@ -27,6 +34,6 @@ urlpatterns = [
|
|||||||
views.empty_view,
|
views.empty_view,
|
||||||
name="regex_only_optional",
|
name="regex_only_optional",
|
||||||
),
|
),
|
||||||
path("", include("urlpatterns.more_urls")),
|
path("", include("urlpatterns.more_urls"), {"sub-extra": False}),
|
||||||
path("<lang>/<path:url>/", views.empty_view, name="lang-and-path"),
|
path("<lang>/<path:url>/", views.empty_view, name="lang-and-path"),
|
||||||
]
|
]
|
||||||
|
@ -34,6 +34,8 @@ class SimplifiedURLTests(SimpleTestCase):
|
|||||||
self.assertEqual(match.args, ())
|
self.assertEqual(match.args, ())
|
||||||
self.assertEqual(match.kwargs, {})
|
self.assertEqual(match.kwargs, {})
|
||||||
self.assertEqual(match.route, "articles/2003/")
|
self.assertEqual(match.route, "articles/2003/")
|
||||||
|
self.assertEqual(match.captured_kwargs, {})
|
||||||
|
self.assertEqual(match.extra_kwargs, {})
|
||||||
|
|
||||||
def test_path_lookup_with_typed_parameters(self):
|
def test_path_lookup_with_typed_parameters(self):
|
||||||
match = resolve("/articles/2015/")
|
match = resolve("/articles/2015/")
|
||||||
@ -41,6 +43,8 @@ class SimplifiedURLTests(SimpleTestCase):
|
|||||||
self.assertEqual(match.args, ())
|
self.assertEqual(match.args, ())
|
||||||
self.assertEqual(match.kwargs, {"year": 2015})
|
self.assertEqual(match.kwargs, {"year": 2015})
|
||||||
self.assertEqual(match.route, "articles/<int:year>/")
|
self.assertEqual(match.route, "articles/<int:year>/")
|
||||||
|
self.assertEqual(match.captured_kwargs, {"year": 2015})
|
||||||
|
self.assertEqual(match.extra_kwargs, {})
|
||||||
|
|
||||||
def test_path_lookup_with_multiple_parameters(self):
|
def test_path_lookup_with_multiple_parameters(self):
|
||||||
match = resolve("/articles/2015/04/12/")
|
match = resolve("/articles/2015/04/12/")
|
||||||
@ -48,18 +52,44 @@ class SimplifiedURLTests(SimpleTestCase):
|
|||||||
self.assertEqual(match.args, ())
|
self.assertEqual(match.args, ())
|
||||||
self.assertEqual(match.kwargs, {"year": 2015, "month": 4, "day": 12})
|
self.assertEqual(match.kwargs, {"year": 2015, "month": 4, "day": 12})
|
||||||
self.assertEqual(match.route, "articles/<int:year>/<int:month>/<int:day>/")
|
self.assertEqual(match.route, "articles/<int:year>/<int:month>/<int:day>/")
|
||||||
|
self.assertEqual(match.captured_kwargs, {"year": 2015, "month": 4, "day": 12})
|
||||||
|
self.assertEqual(match.extra_kwargs, {})
|
||||||
|
|
||||||
|
def test_path_lookup_with_multiple_parameters_and_extra_kwarg(self):
|
||||||
|
match = resolve("/books/2015/04/12/")
|
||||||
|
self.assertEqual(match.url_name, "books-year-month-day")
|
||||||
|
self.assertEqual(match.args, ())
|
||||||
|
self.assertEqual(
|
||||||
|
match.kwargs, {"year": 2015, "month": 4, "day": 12, "extra": True}
|
||||||
|
)
|
||||||
|
self.assertEqual(match.route, "books/<int:year>/<int:month>/<int:day>/")
|
||||||
|
self.assertEqual(match.captured_kwargs, {"year": 2015, "month": 4, "day": 12})
|
||||||
|
self.assertEqual(match.extra_kwargs, {"extra": True})
|
||||||
|
|
||||||
|
def test_path_lookup_with_extra_kwarg(self):
|
||||||
|
match = resolve("/books/2007/")
|
||||||
|
self.assertEqual(match.url_name, "books-2007")
|
||||||
|
self.assertEqual(match.args, ())
|
||||||
|
self.assertEqual(match.kwargs, {"extra": True})
|
||||||
|
self.assertEqual(match.route, "books/2007/")
|
||||||
|
self.assertEqual(match.captured_kwargs, {})
|
||||||
|
self.assertEqual(match.extra_kwargs, {"extra": True})
|
||||||
|
|
||||||
def test_two_variable_at_start_of_path_pattern(self):
|
def test_two_variable_at_start_of_path_pattern(self):
|
||||||
match = resolve("/en/foo/")
|
match = resolve("/en/foo/")
|
||||||
self.assertEqual(match.url_name, "lang-and-path")
|
self.assertEqual(match.url_name, "lang-and-path")
|
||||||
self.assertEqual(match.kwargs, {"lang": "en", "url": "foo"})
|
self.assertEqual(match.kwargs, {"lang": "en", "url": "foo"})
|
||||||
self.assertEqual(match.route, "<lang>/<path:url>/")
|
self.assertEqual(match.route, "<lang>/<path:url>/")
|
||||||
|
self.assertEqual(match.captured_kwargs, {"lang": "en", "url": "foo"})
|
||||||
|
self.assertEqual(match.extra_kwargs, {})
|
||||||
|
|
||||||
def test_re_path(self):
|
def test_re_path(self):
|
||||||
match = resolve("/regex/1/")
|
match = resolve("/regex/1/")
|
||||||
self.assertEqual(match.url_name, "regex")
|
self.assertEqual(match.url_name, "regex")
|
||||||
self.assertEqual(match.kwargs, {"pk": "1"})
|
self.assertEqual(match.kwargs, {"pk": "1"})
|
||||||
self.assertEqual(match.route, "^regex/(?P<pk>[0-9]+)/$")
|
self.assertEqual(match.route, "^regex/(?P<pk>[0-9]+)/$")
|
||||||
|
self.assertEqual(match.captured_kwargs, {"pk": "1"})
|
||||||
|
self.assertEqual(match.extra_kwargs, {})
|
||||||
|
|
||||||
def test_re_path_with_optional_parameter(self):
|
def test_re_path_with_optional_parameter(self):
|
||||||
for url, kwargs in (
|
for url, kwargs in (
|
||||||
@ -74,6 +104,8 @@ class SimplifiedURLTests(SimpleTestCase):
|
|||||||
match.route,
|
match.route,
|
||||||
r"^regex_optional/(?P<arg1>\d+)/(?:(?P<arg2>\d+)/)?",
|
r"^regex_optional/(?P<arg1>\d+)/(?:(?P<arg2>\d+)/)?",
|
||||||
)
|
)
|
||||||
|
self.assertEqual(match.captured_kwargs, kwargs)
|
||||||
|
self.assertEqual(match.extra_kwargs, {})
|
||||||
|
|
||||||
def test_re_path_with_missing_optional_parameter(self):
|
def test_re_path_with_missing_optional_parameter(self):
|
||||||
match = resolve("/regex_only_optional/")
|
match = resolve("/regex_only_optional/")
|
||||||
@ -84,6 +116,8 @@ class SimplifiedURLTests(SimpleTestCase):
|
|||||||
match.route,
|
match.route,
|
||||||
r"^regex_only_optional/(?:(?P<arg1>\d+)/)?",
|
r"^regex_only_optional/(?:(?P<arg1>\d+)/)?",
|
||||||
)
|
)
|
||||||
|
self.assertEqual(match.captured_kwargs, {})
|
||||||
|
self.assertEqual(match.extra_kwargs, {})
|
||||||
|
|
||||||
def test_path_lookup_with_inclusion(self):
|
def test_path_lookup_with_inclusion(self):
|
||||||
match = resolve("/included_urls/extra/something/")
|
match = resolve("/included_urls/extra/something/")
|
||||||
@ -94,6 +128,9 @@ class SimplifiedURLTests(SimpleTestCase):
|
|||||||
match = resolve("/more/99/")
|
match = resolve("/more/99/")
|
||||||
self.assertEqual(match.url_name, "inner-more")
|
self.assertEqual(match.url_name, "inner-more")
|
||||||
self.assertEqual(match.route, r"^more/(?P<extra>\w+)/$")
|
self.assertEqual(match.route, r"^more/(?P<extra>\w+)/$")
|
||||||
|
self.assertEqual(match.kwargs, {"extra": "99", "sub-extra": True})
|
||||||
|
self.assertEqual(match.captured_kwargs, {"extra": "99"})
|
||||||
|
self.assertEqual(match.extra_kwargs, {"sub-extra": True})
|
||||||
|
|
||||||
def test_path_lookup_with_double_inclusion(self):
|
def test_path_lookup_with_double_inclusion(self):
|
||||||
match = resolve("/included_urls/more/some_value/")
|
match = resolve("/included_urls/more/some_value/")
|
||||||
|
@ -23,7 +23,10 @@ urlpatterns = [
|
|||||||
path("resolver_match/", views.pass_resolver_match_view, name="test-resolver-match"),
|
path("resolver_match/", views.pass_resolver_match_view, name="test-resolver-match"),
|
||||||
re_path(r"^\+\\\$\*/$", views.empty_view, name="special-view"),
|
re_path(r"^\+\\\$\*/$", views.empty_view, name="special-view"),
|
||||||
re_path(
|
re_path(
|
||||||
r"^mixed_args/([0-9]+)/(?P<arg2>[0-9]+)/$", views.empty_view, name="mixed-args"
|
r"^mixed_args/([0-9]+)/(?P<arg2>[0-9]+)/$",
|
||||||
|
views.empty_view,
|
||||||
|
{"extra": True},
|
||||||
|
name="mixed-args",
|
||||||
),
|
),
|
||||||
re_path(r"^no_kwargs/([0-9]+)/([0-9]+)/$", views.empty_view, name="no-kwargs"),
|
re_path(r"^no_kwargs/([0-9]+)/([0-9]+)/$", views.empty_view, name="no-kwargs"),
|
||||||
re_path(
|
re_path(
|
||||||
|
@ -89,7 +89,7 @@ resolve_test_data = (
|
|||||||
"mixed-args",
|
"mixed-args",
|
||||||
views.empty_view,
|
views.empty_view,
|
||||||
(),
|
(),
|
||||||
{"arg2": "37"},
|
{"extra": True, "arg2": "37"},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"/included/mixed_args/42/37/",
|
"/included/mixed_args/42/37/",
|
||||||
@ -1554,6 +1554,16 @@ class ResolverMatchTests(SimpleTestCase):
|
|||||||
"namespaces=[], route='^no_kwargs/([0-9]+)/([0-9]+)/$')",
|
"namespaces=[], route='^no_kwargs/([0-9]+)/([0-9]+)/$')",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_repr_extra_kwargs(self):
|
||||||
|
self.assertEqual(
|
||||||
|
repr(resolve("/mixed_args/1986/11/")),
|
||||||
|
"ResolverMatch(func=urlpatterns_reverse.views.empty_view, args=(), "
|
||||||
|
"kwargs={'arg2': '11', 'extra': True}, url_name='mixed-args', "
|
||||||
|
"app_names=[], namespaces=[], "
|
||||||
|
"route='^mixed_args/([0-9]+)/(?P<arg2>[0-9]+)/$', "
|
||||||
|
"captured_kwargs={'arg2': '11'}, extra_kwargs={'extra': True})",
|
||||||
|
)
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF="urlpatterns_reverse.reverse_lazy_urls")
|
@override_settings(ROOT_URLCONF="urlpatterns_reverse.reverse_lazy_urls")
|
||||||
def test_classbased_repr(self):
|
def test_classbased_repr(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@ -1758,3 +1768,18 @@ class LookaheadTests(SimpleTestCase):
|
|||||||
with self.subTest(name=name, kwargs=kwargs):
|
with self.subTest(name=name, kwargs=kwargs):
|
||||||
with self.assertRaises(NoReverseMatch):
|
with self.assertRaises(NoReverseMatch):
|
||||||
reverse(name, kwargs=kwargs)
|
reverse(name, kwargs=kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(ROOT_URLCONF="urlpatterns_reverse.urls")
|
||||||
|
class ReverseResolvedTests(SimpleTestCase):
|
||||||
|
def test_rereverse(self):
|
||||||
|
match = resolve("/resolved/12/")
|
||||||
|
self.assertEqual(
|
||||||
|
reverse(match.url_name, args=match.args, kwargs=match.kwargs),
|
||||||
|
"/resolved/12/",
|
||||||
|
)
|
||||||
|
match = resolve("/resolved-overridden/12/url/")
|
||||||
|
self.assertEqual(
|
||||||
|
reverse(match.url_name, args=match.args, kwargs=match.captured_kwargs),
|
||||||
|
"/resolved-overridden/12/url/",
|
||||||
|
)
|
||||||
|
@ -78,6 +78,13 @@ urlpatterns = [
|
|||||||
name="windows",
|
name="windows",
|
||||||
),
|
),
|
||||||
re_path(r"^special_chars/(?P<chars>.+)/$", empty_view, name="special"),
|
re_path(r"^special_chars/(?P<chars>.+)/$", empty_view, name="special"),
|
||||||
|
re_path(r"^resolved/(?P<arg>\d+)/$", empty_view, {"extra": True}, name="resolved"),
|
||||||
|
re_path(
|
||||||
|
r"^resolved-overridden/(?P<arg>\d+)/(?P<overridden>\w+)/$",
|
||||||
|
empty_view,
|
||||||
|
{"extra": True, "overridden": "default"},
|
||||||
|
name="resolved-overridden",
|
||||||
|
),
|
||||||
re_path(r"^(?P<name>.+)/[0-9]+/$", empty_view, name="mixed"),
|
re_path(r"^(?P<name>.+)/[0-9]+/$", empty_view, name="mixed"),
|
||||||
re_path(r"^repeats/a{1,2}/$", empty_view, name="repeats"),
|
re_path(r"^repeats/a{1,2}/$", empty_view, name="repeats"),
|
||||||
re_path(r"^repeats/a{2,4}/$", empty_view, name="repeats2"),
|
re_path(r"^repeats/a{2,4}/$", empty_view, name="repeats2"),
|
||||||
|
Loading…
Reference in New Issue
Block a user