1
0
mirror of https://github.com/django/django.git synced 2025-06-05 03:29:12 +00:00

Fixed #36268 -- Added leading ? in every querystring template tag result.

Thanks Sarah Boyce for the report.
This commit is contained in:
Natalia 2025-03-20 11:32:54 -03:00 committed by nessita
parent b1c1fd33ed
commit 0b4f2d8d39
4 changed files with 31 additions and 16 deletions

View File

@ -1182,6 +1182,8 @@ def querystring(context, query_dict=None, **kwargs):
`request.GET`). Keyword arguments are processed sequentially, with later `request.GET`). Keyword arguments are processed sequentially, with later
arguments taking precedence. arguments taking precedence.
A query string prefixed with `?` is returned.
For example:: For example::
{# Set a parameter on top of `request.GET` #} {# Set a parameter on top of `request.GET` #}
@ -1207,9 +1209,7 @@ def querystring(context, query_dict=None, **kwargs):
params.setlist(key, value) params.setlist(key, value)
else: else:
params[key] = value params[key] = value
if not params and not query_dict: query_string = params.urlencode() if params else ""
return ""
query_string = params.urlencode()
return f"?{query_string}" return f"?{query_string}"

View File

@ -967,9 +967,8 @@ Outputs a URL-encoded formatted query string based on the provided parameters.
This tag requires a :class:`~django.http.QueryDict` instance, which defaults to This tag requires a :class:`~django.http.QueryDict` instance, which defaults to
:attr:`request.GET <django.http.HttpRequest.GET>` if none is provided. :attr:`request.GET <django.http.HttpRequest.GET>` if none is provided.
If the :class:`~django.http.QueryDict` is empty and no additional parameters The result always includes a leading ``"?"`` since this tag is mainly used for
are provided, an empty string is returned. Otherwise, the result includes a links, and an empty result could prevent the page from reloading as expected.
leading ``"?"``.
.. admonition:: Using ``request.GET`` as default .. admonition:: Using ``request.GET`` as default
@ -979,6 +978,10 @@ leading ``"?"``.
``request`` object into the template context, or provide a ``QueryDict`` ``request`` object into the template context, or provide a ``QueryDict``
instance to this tag. instance to this tag.
.. versionchanged:: 6.0
A ``?`` was prepended to the query string for empty results.
Basic usage Basic usage
~~~~~~~~~~~ ~~~~~~~~~~~
@ -986,8 +989,9 @@ Basic usage
{% querystring %} {% querystring %}
Outputs the current query string verbatim. So if the query string is Outputs the current query string verbatim. So if the query string in the
``?color=green``, the output would be ``?color=green``. request is ``?color=green``, the output would be ``?color=green``. If the
current query string is empty, the output will be ``?``.
.. code-block:: html+django .. code-block:: html+django
@ -1038,7 +1042,7 @@ Custom QueryDict
You can provide a custom ``QueryDict`` to be used instead of ``request.GET``. You can provide a custom ``QueryDict`` to be used instead of ``request.GET``.
So if ``my_query_dict`` is ``<QueryDict: {'color': ['blue']}>``, this outputs So if ``my_query_dict`` is ``<QueryDict: {'color': ['blue']}>``, this outputs
``?color=blue``. ``?color=blue``. If ``my_query_dict`` is empty, the output will be ``?``.
Dynamic usage Dynamic usage
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -233,6 +233,9 @@ Templates
* The new variable ``forloop.length`` is now available within a :ttag:`for` * The new variable ``forloop.length`` is now available within a :ttag:`for`
loop. loop.
* The :ttag:`querystring` template tag now consistently prefixes the returned
query string with a ``?``, ensuring reliable link generation behavior.
Tests Tests
~~~~~ ~~~~~

View File

@ -16,17 +16,15 @@ class QueryStringTagTests(SimpleTestCase):
@setup({"querystring_empty_get_params": "{% querystring %}"}) @setup({"querystring_empty_get_params": "{% querystring %}"})
def test_querystring_empty_get_params(self): def test_querystring_empty_get_params(self):
context = RequestContext(self.request_factory.get("/")) context = RequestContext(self.request_factory.get("/"))
self.assertRenderEqual("querystring_empty_get_params", context, expected="") self.assertRenderEqual("querystring_empty_get_params", context, expected="?")
@setup({"querystring_remove_all_params": "{% querystring a=None %}"}) @setup({"querystring_remove_all_params": "{% querystring a=None %}"})
def test_querystring_remove_all_params(self): def test_querystring_remove_all_params(self):
non_empty_context = RequestContext(self.request_factory.get("/?a=b")) non_empty_context = RequestContext(self.request_factory.get("/?a=b"))
empty_context = RequestContext(self.request_factory.get("/")) empty_context = RequestContext(self.request_factory.get("/"))
for context, expected in [(non_empty_context, "?"), (empty_context, "")]: for context in [non_empty_context, empty_context]:
with self.subTest(expected=expected): with self.subTest(context=context):
self.assertRenderEqual( self.assertRenderEqual("querystring_remove_all_params", context, "?")
"querystring_remove_all_params", context, expected
)
@setup({"querystring_non_empty_get_params": "{% querystring %}"}) @setup({"querystring_non_empty_get_params": "{% querystring %}"})
def test_querystring_non_empty_get_params(self): def test_querystring_non_empty_get_params(self):
@ -46,10 +44,20 @@ class QueryStringTagTests(SimpleTestCase):
def test_querystring_empty_params(self): def test_querystring_empty_params(self):
cases = [None, {}, QueryDict()] cases = [None, {}, QueryDict()]
request = self.request_factory.get("/") request = self.request_factory.get("/")
qs = "?a=b"
request_with_qs = self.request_factory.get(f"/{qs}")
for param in cases: for param in cases:
# Empty `query_dict` and nothing on `request.GET`.
with self.subTest(param=param): with self.subTest(param=param):
context = RequestContext(request, {"qd": param}) context = RequestContext(request, {"qd": param})
self.assertRenderEqual("querystring_empty_params", context, expected="") self.assertRenderEqual(
"querystring_empty_params", context, expected="?"
)
# Empty `query_dict` and a query string in `request.GET`.
with self.subTest(param=param, qs=qs):
context = RequestContext(request_with_qs, {"qd": param})
expected = "?" if param is not None else qs
self.assertRenderEqual("querystring_empty_params", context, expected)
@setup({"querystring_replace": "{% querystring a=1 %}"}) @setup({"querystring_replace": "{% querystring a=1 %}"})
def test_querystring_replace(self): def test_querystring_replace(self):