From 231c0d85931b5afde3e3caec0e6bc5ca6132bb7a Mon Sep 17 00:00:00 2001 From: nabil-rady Date: Wed, 14 Aug 2024 00:58:37 +0300 Subject: [PATCH] Fixed #35668 -- Added mapping support to format_html_join. --- django/utils/html.py | 8 +++++++- docs/ref/utils.txt | 25 ++++++++++++++++++++++--- docs/releases/5.2.txt | 4 ++++ tests/utils_tests/test_html.py | 21 +++++++++++++++++++++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/django/utils/html.py b/django/utils/html.py index 154c820d34..3ad920aca0 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -4,6 +4,7 @@ import html import json import re import warnings +from collections.abc import Mapping from html.parser import HTMLParser from urllib.parse import parse_qsl, quote, unquote, urlencode, urlsplit, urlunsplit @@ -155,7 +156,12 @@ def format_html_join(sep, format_string, args_generator): """ return mark_safe( conditional_escape(sep).join( - format_html(format_string, *args) for args in args_generator + ( + format_html(format_string, **args) + if isinstance(args, Mapping) + else format_html(format_string, *args) + ) + for args in args_generator ) ) diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index d6c70a9bb0..33e0fceadf 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -699,10 +699,29 @@ escaping HTML. joined using ``sep``. ``sep`` is also passed through :func:`conditional_escape`. - ``args_generator`` should be an iterator that returns the sequence of - ``args`` that will be passed to :func:`format_html`. For example:: + ``args_generator`` should be an iterator that yields arguments to pass to + :func:`format_html`, either sequences of positional arguments or mappings of + keyword arguments. - format_html_join("\n", "
  • {} {}
  • ", ((u.first_name, u.last_name) for u in users)) + For example, tuples can be used for positional arguments:: + + format_html_join( + "\n", + "
  • {} {}
  • ", + ((u.first_name, u.last_name) for u in users), + ) + + Or dictionaries can be used for keyword arguments:: + + format_html_join( + "\n", + '
  • {id} {title}
  • ', + ({"id": b.id, "title": b.title} for b in books), + ) + + .. versionchanged:: 5.2 + + Support for mappings in ``args_generator`` was added. .. function:: json_script(value, element_id=None, encoder=None) diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt index fa25737432..0caeef01cf 100644 --- a/docs/releases/5.2.txt +++ b/docs/releases/5.2.txt @@ -267,6 +267,10 @@ Utilities values. This aligns with the :py:class:`str` addition behavior and allows ``__radd__`` to be used if available. +* :func:`~django.utils.html.format_html_join` now supports taking an iterable + of mappings, passing their contents as keyword arguments to + :func:`~django.utils.html.format_html`. + Validators ~~~~~~~~~~ diff --git a/tests/utils_tests/test_html.py b/tests/utils_tests/test_html.py index 82dbd58f12..f6373e3048 100644 --- a/tests/utils_tests/test_html.py +++ b/tests/utils_tests/test_html.py @@ -10,6 +10,7 @@ from django.utils.html import ( escape, escapejs, format_html, + format_html_join, html_safe, json_script, linebreaks, @@ -75,6 +76,26 @@ class TestUtilsHtml(SimpleTestCase): name = "Adam" self.assertEqual(format_html(f"{name}"), "Adam") + def test_format_html_join_with_positional_arguments(self): + self.assertEqual( + format_html_join( + "\n", + "
  • {}) {}
  • ", + [(1, "Emma"), (2, "Matilda")], + ), + "
  • 1) Emma
  • \n
  • 2) Matilda
  • ", + ) + + def test_format_html_join_with_keyword_arguments(self): + self.assertEqual( + format_html_join( + "\n", + "
  • {id}) {text}
  • ", + [{"id": 1, "text": "Emma"}, {"id": 2, "text": "Matilda"}], + ), + "
  • 1) Emma
  • \n
  • 2) Matilda
  • ", + ) + def test_linebreaks(self): items = ( ("para1\n\npara2\r\rpara3", "

    para1

    \n\n

    para2

    \n\n

    para3

    "),