1. from django.template import Context, Engine
    
  2. from django.test import SimpleTestCase
    
  3. 
    
  4. from ..utils import setup
    
  5. 
    
  6. 
    
  7. class IfChangedTagTests(SimpleTestCase):
    
  8.     libraries = {"custom": "template_tests.templatetags.custom"}
    
  9. 
    
  10.     @setup(
    
  11.         {
    
  12.             "ifchanged01": (
    
  13.                 "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}"
    
  14.             )
    
  15.         }
    
  16.     )
    
  17.     def test_ifchanged01(self):
    
  18.         output = self.engine.render_to_string("ifchanged01", {"num": (1, 2, 3)})
    
  19.         self.assertEqual(output, "123")
    
  20. 
    
  21.     @setup(
    
  22.         {
    
  23.             "ifchanged02": (
    
  24.                 "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}"
    
  25.             )
    
  26.         }
    
  27.     )
    
  28.     def test_ifchanged02(self):
    
  29.         output = self.engine.render_to_string("ifchanged02", {"num": (1, 1, 3)})
    
  30.         self.assertEqual(output, "13")
    
  31. 
    
  32.     @setup(
    
  33.         {
    
  34.             "ifchanged03": (
    
  35.                 "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}"
    
  36.             )
    
  37.         }
    
  38.     )
    
  39.     def test_ifchanged03(self):
    
  40.         output = self.engine.render_to_string("ifchanged03", {"num": (1, 1, 1)})
    
  41.         self.assertEqual(output, "1")
    
  42. 
    
  43.     @setup(
    
  44.         {
    
  45.             "ifchanged04": "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}"
    
  46.             "{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}"
    
  47.             "{% endfor %}{% endfor %}"
    
  48.         }
    
  49.     )
    
  50.     def test_ifchanged04(self):
    
  51.         output = self.engine.render_to_string(
    
  52.             "ifchanged04", {"num": (1, 2, 3), "numx": (2, 2, 2)}
    
  53.         )
    
  54.         self.assertEqual(output, "122232")
    
  55. 
    
  56.     @setup(
    
  57.         {
    
  58.             "ifchanged05": "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}"
    
  59.             "{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}"
    
  60.             "{% endfor %}{% endfor %}"
    
  61.         }
    
  62.     )
    
  63.     def test_ifchanged05(self):
    
  64.         output = self.engine.render_to_string(
    
  65.             "ifchanged05", {"num": (1, 1, 1), "numx": (1, 2, 3)}
    
  66.         )
    
  67.         self.assertEqual(output, "1123123123")
    
  68. 
    
  69.     @setup(
    
  70.         {
    
  71.             "ifchanged06": "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}"
    
  72.             "{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}"
    
  73.             "{% endfor %}{% endfor %}"
    
  74.         }
    
  75.     )
    
  76.     def test_ifchanged06(self):
    
  77.         output = self.engine.render_to_string(
    
  78.             "ifchanged06", {"num": (1, 1, 1), "numx": (2, 2, 2)}
    
  79.         )
    
  80.         self.assertEqual(output, "1222")
    
  81. 
    
  82.     @setup(
    
  83.         {
    
  84.             "ifchanged07": "{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}"
    
  85.             "{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}"
    
  86.             "{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}"
    
  87.             "{% endfor %}{% endfor %}{% endfor %}"
    
  88.         }
    
  89.     )
    
  90.     def test_ifchanged07(self):
    
  91.         output = self.engine.render_to_string(
    
  92.             "ifchanged07", {"num": (1, 1, 1), "numx": (2, 2, 2), "numy": (3, 3, 3)}
    
  93.         )
    
  94.         self.assertEqual(output, "1233323332333")
    
  95. 
    
  96.     @setup(
    
  97.         {
    
  98.             "ifchanged08": "{% for data in datalist %}{% for c,d in data %}"
    
  99.             "{% if c %}{% ifchanged %}{{ d }}{% endifchanged %}"
    
  100.             "{% endif %}{% endfor %}{% endfor %}"
    
  101.         }
    
  102.     )
    
  103.     def test_ifchanged08(self):
    
  104.         output = self.engine.render_to_string(
    
  105.             "ifchanged08",
    
  106.             {
    
  107.                 "datalist": [
    
  108.                     [(1, "a"), (1, "a"), (0, "b"), (1, "c")],
    
  109.                     [(0, "a"), (1, "c"), (1, "d"), (1, "d"), (0, "e")],
    
  110.                 ]
    
  111.             },
    
  112.         )
    
  113.         self.assertEqual(output, "accd")
    
  114. 
    
  115.     @setup(
    
  116.         {
    
  117.             "ifchanged-param01": (
    
  118.                 "{% for n in num %}{% ifchanged n %}..{% endifchanged %}"
    
  119.                 "{{ n }}{% endfor %}"
    
  120.             )
    
  121.         }
    
  122.     )
    
  123.     def test_ifchanged_param01(self):
    
  124.         """
    
  125.         Test one parameter given to ifchanged.
    
  126.         """
    
  127.         output = self.engine.render_to_string("ifchanged-param01", {"num": (1, 2, 3)})
    
  128.         self.assertEqual(output, "..1..2..3")
    
  129. 
    
  130.     @setup(
    
  131.         {
    
  132.             "ifchanged-param02": (
    
  133.                 "{% for n in num %}{% for x in numx %}"
    
  134.                 "{% ifchanged n %}..{% endifchanged %}{{ x }}"
    
  135.                 "{% endfor %}{% endfor %}"
    
  136.             )
    
  137.         }
    
  138.     )
    
  139.     def test_ifchanged_param02(self):
    
  140.         output = self.engine.render_to_string(
    
  141.             "ifchanged-param02", {"num": (1, 2, 3), "numx": (5, 6, 7)}
    
  142.         )
    
  143.         self.assertEqual(output, "..567..567..567")
    
  144. 
    
  145.     @setup(
    
  146.         {
    
  147.             "ifchanged-param03": "{% for n in num %}{{ n }}{% for x in numx %}"
    
  148.             "{% ifchanged x n %}{{ x }}{% endifchanged %}"
    
  149.             "{% endfor %}{% endfor %}"
    
  150.         }
    
  151.     )
    
  152.     def test_ifchanged_param03(self):
    
  153.         """
    
  154.         Test multiple parameters to ifchanged.
    
  155.         """
    
  156.         output = self.engine.render_to_string(
    
  157.             "ifchanged-param03", {"num": (1, 1, 2), "numx": (5, 6, 6)}
    
  158.         )
    
  159.         self.assertEqual(output, "156156256")
    
  160. 
    
  161.     @setup(
    
  162.         {
    
  163.             "ifchanged-param04": (
    
  164.                 "{% for d in days %}{% ifchanged %}{{ d.day }}{% endifchanged %}"
    
  165.                 "{% for h in d.hours %}{% ifchanged d h %}{{ h }}{% endifchanged %}"
    
  166.                 "{% endfor %}{% endfor %}"
    
  167.             )
    
  168.         }
    
  169.     )
    
  170.     def test_ifchanged_param04(self):
    
  171.         """
    
  172.         Test a date+hour like construct, where the hour of the last day is
    
  173.         the same but the date had changed, so print the hour anyway.
    
  174.         """
    
  175.         output = self.engine.render_to_string(
    
  176.             "ifchanged-param04",
    
  177.             {"days": [{"hours": [1, 2, 3], "day": 1}, {"hours": [3], "day": 2}]},
    
  178.         )
    
  179.         self.assertEqual(output, "112323")
    
  180. 
    
  181.     @setup(
    
  182.         {
    
  183.             "ifchanged-param05": (
    
  184.                 "{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}"
    
  185.                 "{% for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}"
    
  186.                 "{% endfor %}{% endfor %}"
    
  187.             )
    
  188.         }
    
  189.     )
    
  190.     def test_ifchanged_param05(self):
    
  191.         """
    
  192.         Logically the same as above, just written with explicit ifchanged
    
  193.         for the day.
    
  194.         """
    
  195.         output = self.engine.render_to_string(
    
  196.             "ifchanged-param05",
    
  197.             {"days": [{"hours": [1, 2, 3], "day": 1}, {"hours": [3], "day": 2}]},
    
  198.         )
    
  199.         self.assertEqual(output, "112323")
    
  200. 
    
  201.     @setup(
    
  202.         {
    
  203.             "ifchanged-else01": "{% for id in ids %}{{ id }}"
    
  204.             "{% ifchanged id %}-first{% else %}-other{% endifchanged %}"
    
  205.             ",{% endfor %}"
    
  206.         }
    
  207.     )
    
  208.     def test_ifchanged_else01(self):
    
  209.         """
    
  210.         Test the else clause of ifchanged.
    
  211.         """
    
  212.         output = self.engine.render_to_string(
    
  213.             "ifchanged-else01", {"ids": [1, 1, 2, 2, 2, 3]}
    
  214.         )
    
  215.         self.assertEqual(output, "1-first,1-other,2-first,2-other,2-other,3-first,")
    
  216. 
    
  217.     @setup(
    
  218.         {
    
  219.             "ifchanged-else02": "{% for id in ids %}{{ id }}-"
    
  220.             '{% ifchanged id %}{% cycle "red" "blue" %}{% else %}gray{% endifchanged %}'
    
  221.             ",{% endfor %}"
    
  222.         }
    
  223.     )
    
  224.     def test_ifchanged_else02(self):
    
  225.         output = self.engine.render_to_string(
    
  226.             "ifchanged-else02", {"ids": [1, 1, 2, 2, 2, 3]}
    
  227.         )
    
  228.         self.assertEqual(output, "1-red,1-gray,2-blue,2-gray,2-gray,3-red,")
    
  229. 
    
  230.     @setup(
    
  231.         {
    
  232.             "ifchanged-else03": "{% for id in ids %}{{ id }}"
    
  233.             '{% ifchanged id %}-{% cycle "red" "blue" %}{% else %}{% endifchanged %}'
    
  234.             ",{% endfor %}"
    
  235.         }
    
  236.     )
    
  237.     def test_ifchanged_else03(self):
    
  238.         output = self.engine.render_to_string(
    
  239.             "ifchanged-else03", {"ids": [1, 1, 2, 2, 2, 3]}
    
  240.         )
    
  241.         self.assertEqual(output, "1-red,1,2-blue,2,2,3-red,")
    
  242. 
    
  243.     @setup(
    
  244.         {
    
  245.             "ifchanged-else04": "{% for id in ids %}"
    
  246.             "{% ifchanged %}***{{ id }}*{% else %}...{% endifchanged %}"
    
  247.             "{{ forloop.counter }}{% endfor %}"
    
  248.         }
    
  249.     )
    
  250.     def test_ifchanged_else04(self):
    
  251.         output = self.engine.render_to_string(
    
  252.             "ifchanged-else04", {"ids": [1, 1, 2, 2, 2, 3, 4]}
    
  253.         )
    
  254.         self.assertEqual(output, "***1*1...2***2*3...4...5***3*6***4*7")
    
  255. 
    
  256.     @setup(
    
  257.         {
    
  258.             "ifchanged-filter-ws": "{% load custom %}{% for n in num %}"
    
  259.             '{% ifchanged n|noop:"x y" %}..{% endifchanged %}{{ n }}'
    
  260.             "{% endfor %}"
    
  261.         }
    
  262.     )
    
  263.     def test_ifchanged_filter_ws(self):
    
  264.         """
    
  265.         Test whitespace in filter arguments
    
  266.         """
    
  267.         output = self.engine.render_to_string("ifchanged-filter-ws", {"num": (1, 2, 3)})
    
  268.         self.assertEqual(output, "..1..2..3")
    
  269. 
    
  270. 
    
  271. class IfChangedTests(SimpleTestCase):
    
  272.     @classmethod
    
  273.     def setUpClass(cls):
    
  274.         cls.engine = Engine()
    
  275.         super().setUpClass()
    
  276. 
    
  277.     def test_ifchanged_concurrency(self):
    
  278.         """
    
  279.         #15849 -- ifchanged should be thread-safe.
    
  280.         """
    
  281.         template = self.engine.from_string(
    
  282.             "[0{% for x in foo %},{% with var=get_value %}{% ifchanged %}"
    
  283.             "{{ var }}{% endifchanged %}{% endwith %}{% endfor %}]"
    
  284.         )
    
  285. 
    
  286.         # Using generator to mimic concurrency.
    
  287.         # The generator is not passed to the 'for' loop, because it does a list(values)
    
  288.         # instead, call gen.next() in the template to control the generator.
    
  289.         def gen():
    
  290.             yield 1
    
  291.             yield 2
    
  292.             # Simulate that another thread is now rendering.
    
  293.             # When the IfChangeNode stores state at 'self' it stays at '3' and
    
  294.             # skip the last yielded value below.
    
  295.             iter2 = iter([1, 2, 3])
    
  296.             output2 = template.render(
    
  297.                 Context({"foo": range(3), "get_value": lambda: next(iter2)})
    
  298.             )
    
  299.             self.assertEqual(
    
  300.                 output2,
    
  301.                 "[0,1,2,3]",
    
  302.                 "Expected [0,1,2,3] in second parallel template, got {}".format(
    
  303.                     output2
    
  304.                 ),
    
  305.             )
    
  306.             yield 3
    
  307. 
    
  308.         gen1 = gen()
    
  309.         output1 = template.render(
    
  310.             Context({"foo": range(3), "get_value": lambda: next(gen1)})
    
  311.         )
    
  312.         self.assertEqual(
    
  313.             output1,
    
  314.             "[0,1,2,3]",
    
  315.             "Expected [0,1,2,3] in first template, got {}".format(output1),
    
  316.         )
    
  317. 
    
  318.     def test_ifchanged_render_once(self):
    
  319.         """
    
  320.         #19890. The content of ifchanged template tag was rendered twice.
    
  321.         """
    
  322.         template = self.engine.from_string(
    
  323.             '{% ifchanged %}{% cycle "1st time" "2nd time" %}{% endifchanged %}'
    
  324.         )
    
  325.         output = template.render(Context({}))
    
  326.         self.assertEqual(output, "1st time")
    
  327. 
    
  328.     def test_include(self):
    
  329.         """
    
  330.         #23516 -- This works as a regression test only if the cached loader
    
  331.         isn't used. Hence we don't use the @setup decorator.
    
  332.         """
    
  333.         engine = Engine(
    
  334.             loaders=[
    
  335.                 (
    
  336.                     "django.template.loaders.locmem.Loader",
    
  337.                     {
    
  338.                         "template": (
    
  339.                             '{% for x in vars %}{% include "include" %}{% endfor %}'
    
  340.                         ),
    
  341.                         "include": "{% ifchanged %}{{ x }}{% endifchanged %}",
    
  342.                     },
    
  343.                 ),
    
  344.             ]
    
  345.         )
    
  346.         output = engine.render_to_string("template", {"vars": [1, 1, 2, 2, 3, 3]})
    
  347.         self.assertEqual(output, "123")
    
  348. 
    
  349.     def test_include_state(self):
    
  350.         """Tests the node state for different IncludeNodes (#27974)."""
    
  351.         engine = Engine(
    
  352.             loaders=[
    
  353.                 (
    
  354.                     "django.template.loaders.locmem.Loader",
    
  355.                     {
    
  356.                         "template": (
    
  357.                             '{% for x in vars %}{% include "include" %}'
    
  358.                             '{% include "include" %}{% endfor %}'
    
  359.                         ),
    
  360.                         "include": "{% ifchanged %}{{ x }}{% endifchanged %}",
    
  361.                     },
    
  362.                 ),
    
  363.             ]
    
  364.         )
    
  365.         output = engine.render_to_string("template", {"vars": [1, 1, 2, 2, 3, 3]})
    
  366.         self.assertEqual(output, "112233")