diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 1152452081..9023bcc87d 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -206,7 +206,10 @@ class ForNode(Node): unpack = num_loopvars > 1 # Create a forloop value in the context. We'll update counters on each # iteration just below. - loop_dict = context["forloop"] = {"parentloop": parentloop} + loop_dict = context["forloop"] = { + "parentloop": parentloop, + "length": len_values, + } for i, item in enumerate(values): # Shortcuts for current loop iteration number. loop_dict["counter0"] = i diff --git a/docs/ref/templates/builtins.txt b/docs/ref/templates/builtins.txt index 8851fd50ea..247f5e9890 100644 --- a/docs/ref/templates/builtins.txt +++ b/docs/ref/templates/builtins.txt @@ -423,10 +423,15 @@ Variable Description loop (0-indexed) ``forloop.first`` True if this is the first time through the loop ``forloop.last`` True if this is the last time through the loop +``forloop.length`` The length of the loop ``forloop.parentloop`` For nested loops, this is the loop surrounding the current one ========================== =============================================== +.. versionchanged:: 6.0 + + The variable ``forloop.length`` was added. + ``for`` ... ``empty`` --------------------- diff --git a/docs/releases/6.0.txt b/docs/releases/6.0.txt index d8e35bb557..5d173c981f 100644 --- a/docs/releases/6.0.txt +++ b/docs/releases/6.0.txt @@ -207,7 +207,8 @@ Signals Templates ~~~~~~~~~ -* ... +* The new variable ``forloop.length`` is now available within a :ttag:`for` + loop. Tests ~~~~~ diff --git a/tests/template_tests/syntax_tests/test_for.py b/tests/template_tests/syntax_tests/test_for.py index a47a99ac65..8b8b1c4d27 100644 --- a/tests/template_tests/syntax_tests/test_for.py +++ b/tests/template_tests/syntax_tests/test_for.py @@ -356,6 +356,25 @@ class ForTagTests(SimpleTestCase): with self.assertRaisesMessage(TemplateSyntaxError, msg): self.engine.render_to_string("invalid_for_loop", {"items": (1, 2)}) + @setup( + { + "forloop-length": "{% for val in values %}{{ forloop.length }}{% endfor %}", + "forloop-length-reversed": "{% for val in values reversed %}" + "{{ forloop.length }}{% endfor %}", + } + ) + def test_forloop_length(self): + cases = [ + ([1, 2, 3], "333"), + ([1, 2, 3, 4, 5, 6], "666666"), + ([], ""), + ] + for values, expected_output in cases: + for template in ["forloop-length", "forloop-length-reversed"]: + with self.subTest(expected_output=expected_output, template=template): + output = self.engine.render_to_string(template, {"values": values}) + self.assertEqual(output, expected_output) + class ForNodeTests(SimpleTestCase): def test_repr(self):