summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur Moreira <moreirarthur96@gmail.com>2023-05-19 19:33:51 -0300
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-05-22 09:58:03 +0200
commit061a8a1bd818ca2c8a6493f33cae2379e34e181f (patch)
tree5f5ff4e46207725cf153dc38cb8001b0b969c7b1
parent98f6ada0e2058d67d91fb6c16482411ec2ca0967 (diff)
Fixed #34577 -- Added escapeseq template filter.
-rw-r--r--AUTHORS1
-rw-r--r--django/template/defaultfilters.py10
-rw-r--r--docs/ref/templates/builtins.txt19
-rw-r--r--docs/releases/5.0.txt3
-rw-r--r--tests/template_tests/filter_tests/test_escapeseq.py59
5 files changed, 91 insertions, 1 deletions
diff --git a/AUTHORS b/AUTHORS
index 2e3a91c756..95f24762ee 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -108,6 +108,7 @@ answer newbie questions, and generally made Django that much better:
Arthur <avandorp@gmail.com>
Arthur Jovart <arthur@jovart.com>
Arthur Koziel <http://arthurkoziel.com>
+ Arthur Moreira <moreirarthur96@gmail.com>
Arthur Rio <arthur.rio44@gmail.com>
Arvis Bickovskis <viestards.lists@gmail.com>
Arya Khaligh <bartararya@gmail.com>
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index 5289ef34a0..589ca38414 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -445,6 +445,16 @@ def escape_filter(value):
@register.filter(is_safe=True)
+def escapeseq(value):
+ """
+ An "escape" filter for sequences. Mark each element in the sequence,
+ individually, as a string that should be auto-escaped. Return a list with
+ the results.
+ """
+ return [conditional_escape(obj) for obj in value]
+
+
+@register.filter(is_safe=True)
@stringfilter
def force_escape(value):
"""
diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt
index 3aa20dfc71..695357c5a0 100644
--- a/docs/ref/templates/builtins.txt
+++ b/docs/ref/templates/builtins.txt
@@ -1831,6 +1831,8 @@ For example, you can apply ``escape`` to fields when :ttag:`autoescape` is off:
{{ title|escape }}
{% endautoescape %}
+To escape each element of a sequence, use the :tfilter:`escapeseq` filter.
+
.. templatefilter:: escapejs
``escapejs``
@@ -1849,6 +1851,23 @@ For example:
If ``value`` is ``"testing\r\njavascript 'string\" <b>escaping</b>"``,
the output will be ``"testing\\u000D\\u000Ajavascript \\u0027string\\u0022 \\u003Cb\\u003Eescaping\\u003C/b\\u003E"``.
+.. templatefilter:: escapeseq
+
+``escapeseq``
+-------------
+
+.. versionadded:: 5.0
+
+Applies the :tfilter:`escape` filter to each element of a sequence. Useful in
+conjunction with other filters that operate on sequences, such as
+:tfilter:`join`. For example:
+
+.. code-block:: html+django
+
+ {% autoescape off %}
+ {{ my_list|escapeseq|join:", " }}
+ {% endautoescape %}
+
.. templatefilter:: filesizeformat
``filesizeformat``
diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt
index 611a7bd68b..e6526dd798 100644
--- a/docs/releases/5.0.txt
+++ b/docs/releases/5.0.txt
@@ -345,7 +345,8 @@ Signals
Templates
~~~~~~~~~
-* ...
+* The new :tfilter:`escapeseq` template filter applies :tfilter:`escape` to
+ each element of a sequence.
Tests
~~~~~
diff --git a/tests/template_tests/filter_tests/test_escapeseq.py b/tests/template_tests/filter_tests/test_escapeseq.py
new file mode 100644
index 0000000000..27092f5828
--- /dev/null
+++ b/tests/template_tests/filter_tests/test_escapeseq.py
@@ -0,0 +1,59 @@
+from django.test import SimpleTestCase
+from django.utils.safestring import mark_safe
+
+from ..utils import setup
+
+
+class EscapeseqTests(SimpleTestCase):
+ """
+ The "escapeseq" filter works the same whether autoescape is on or off,
+ and has no effect on strings already marked as safe.
+ """
+
+ @setup(
+ {
+ "escapeseq_basic": (
+ '{{ a|escapeseq|join:", " }} -- {{ b|escapeseq|join:", " }}'
+ ),
+ }
+ )
+ def test_basic(self):
+ output = self.engine.render_to_string(
+ "escapeseq_basic",
+ {"a": ["x&y", "<p>"], "b": [mark_safe("x&y"), mark_safe("<p>")]},
+ )
+ self.assertEqual(output, "x&amp;y, &lt;p&gt; -- x&y, <p>")
+
+ @setup(
+ {
+ "escapeseq_autoescape_off": (
+ '{% autoescape off %}{{ a|escapeseq|join:", " }}'
+ " -- "
+ '{{ b|escapeseq|join:", "}}{% endautoescape %}'
+ )
+ }
+ )
+ def test_autoescape_off(self):
+ output = self.engine.render_to_string(
+ "escapeseq_autoescape_off",
+ {"a": ["x&y", "<p>"], "b": [mark_safe("x&y"), mark_safe("<p>")]},
+ )
+ self.assertEqual(output, "x&amp;y, &lt;p&gt; -- x&y, <p>")
+
+ @setup({"escapeseq_join": '{{ a|escapeseq|join:"<br/>" }}'})
+ def test_chain_join(self):
+ output = self.engine.render_to_string("escapeseq_join", {"a": ["x&y", "<p>"]})
+ self.assertEqual(output, "x&amp;y<br/>&lt;p&gt;")
+
+ @setup(
+ {
+ "escapeseq_join_autoescape_off": (
+ '{% autoescape off %}{{ a|escapeseq|join:"<br/>" }}{% endautoescape %}'
+ ),
+ }
+ )
+ def test_chain_join_autoescape_off(self):
+ output = self.engine.render_to_string(
+ "escapeseq_join_autoescape_off", {"a": ["x&y", "<p>"]}
+ )
+ self.assertEqual(output, "x&amp;y<br/>&lt;p&gt;")