2013-07-01 12:22:27 +00:00
|
|
|
from unittest import TestCase
|
|
|
|
|
2022-10-04 18:11:28 +00:00
|
|
|
from django.db.models.utils import AltersData
|
2015-04-11 23:41:45 +00:00
|
|
|
from django.template import Context, Engine
|
2011-04-19 22:06:19 +00:00
|
|
|
|
2013-11-02 21:34:05 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
class CallableVariablesTests(TestCase):
|
2015-04-11 23:41:45 +00:00
|
|
|
@classmethod
|
|
|
|
def setUpClass(cls):
|
|
|
|
cls.engine = Engine()
|
2017-01-21 13:13:44 +00:00
|
|
|
super().setUpClass()
|
2015-04-11 23:41:45 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def test_callable(self):
|
2017-01-19 07:39:46 +00:00
|
|
|
class Doodad:
|
2011-04-19 22:06:19 +00:00
|
|
|
def __init__(self, value):
|
|
|
|
self.num_calls = 0
|
|
|
|
self.value = value
|
2013-10-22 10:21:07 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def __call__(self):
|
|
|
|
self.num_calls += 1
|
|
|
|
return {"the_value": self.value}
|
|
|
|
|
|
|
|
my_doodad = Doodad(42)
|
2015-04-11 23:41:45 +00:00
|
|
|
c = Context({"my_doodad": my_doodad})
|
2011-04-19 22:06:19 +00:00
|
|
|
|
|
|
|
# We can't access ``my_doodad.value`` in the template, because
|
|
|
|
# ``my_doodad.__call__`` will be invoked first, yielding a dictionary
|
|
|
|
# without a key ``value``.
|
2015-04-11 23:41:45 +00:00
|
|
|
t = self.engine.from_string("{{ my_doodad.value }}")
|
2012-06-07 16:08:47 +00:00
|
|
|
self.assertEqual(t.render(c), "")
|
2011-04-19 22:06:19 +00:00
|
|
|
|
|
|
|
# We can confirm that the doodad has been called
|
|
|
|
self.assertEqual(my_doodad.num_calls, 1)
|
|
|
|
|
|
|
|
# But we can access keys on the dict that's returned
|
|
|
|
# by ``__call__``, instead.
|
2015-04-11 23:41:45 +00:00
|
|
|
t = self.engine.from_string("{{ my_doodad.the_value }}")
|
2012-06-07 16:08:47 +00:00
|
|
|
self.assertEqual(t.render(c), "42")
|
2011-04-19 22:06:19 +00:00
|
|
|
self.assertEqual(my_doodad.num_calls, 2)
|
|
|
|
|
|
|
|
def test_alters_data(self):
|
2017-01-19 07:39:46 +00:00
|
|
|
class Doodad:
|
2011-04-19 22:06:19 +00:00
|
|
|
alters_data = True
|
2013-10-22 10:21:07 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def __init__(self, value):
|
|
|
|
self.num_calls = 0
|
|
|
|
self.value = value
|
2013-10-22 10:21:07 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def __call__(self):
|
|
|
|
self.num_calls += 1
|
|
|
|
return {"the_value": self.value}
|
|
|
|
|
|
|
|
my_doodad = Doodad(42)
|
2015-04-11 23:41:45 +00:00
|
|
|
c = Context({"my_doodad": my_doodad})
|
2011-04-19 22:06:19 +00:00
|
|
|
|
|
|
|
# Since ``my_doodad.alters_data`` is True, the template system will not
|
2014-12-14 22:13:03 +00:00
|
|
|
# try to call our doodad but will use string_if_invalid
|
2015-04-11 23:41:45 +00:00
|
|
|
t = self.engine.from_string("{{ my_doodad.value }}")
|
2012-06-07 16:08:47 +00:00
|
|
|
self.assertEqual(t.render(c), "")
|
2015-04-11 23:41:45 +00:00
|
|
|
t = self.engine.from_string("{{ my_doodad.the_value }}")
|
2012-06-07 16:08:47 +00:00
|
|
|
self.assertEqual(t.render(c), "")
|
2011-04-19 22:06:19 +00:00
|
|
|
|
|
|
|
# Double-check that the object was really never called during the
|
|
|
|
# template rendering.
|
|
|
|
self.assertEqual(my_doodad.num_calls, 0)
|
|
|
|
|
2022-10-04 18:11:28 +00:00
|
|
|
def test_alters_data_propagation(self):
|
|
|
|
class GrandParentLeft(AltersData):
|
|
|
|
def my_method(self):
|
|
|
|
return 42
|
|
|
|
|
|
|
|
my_method.alters_data = True
|
|
|
|
|
|
|
|
class ParentLeft(GrandParentLeft):
|
|
|
|
def change_alters_data_method(self):
|
|
|
|
return 63
|
|
|
|
|
|
|
|
change_alters_data_method.alters_data = True
|
|
|
|
|
|
|
|
def sub_non_callable_method(self):
|
|
|
|
return 64
|
|
|
|
|
|
|
|
sub_non_callable_method.alters_data = True
|
|
|
|
|
|
|
|
class ParentRight(AltersData):
|
|
|
|
def other_method(self):
|
|
|
|
return 52
|
|
|
|
|
|
|
|
other_method.alters_data = True
|
|
|
|
|
|
|
|
class Child(ParentLeft, ParentRight):
|
|
|
|
def my_method(self):
|
|
|
|
return 101
|
|
|
|
|
|
|
|
def other_method(self):
|
|
|
|
return 102
|
|
|
|
|
|
|
|
def change_alters_data_method(self):
|
|
|
|
return 103
|
|
|
|
|
|
|
|
change_alters_data_method.alters_data = False
|
|
|
|
|
|
|
|
sub_non_callable_method = 104
|
|
|
|
|
|
|
|
class GrandChild(Child):
|
|
|
|
pass
|
|
|
|
|
|
|
|
child = Child()
|
|
|
|
self.assertIs(child.my_method.alters_data, True)
|
|
|
|
self.assertIs(child.other_method.alters_data, True)
|
|
|
|
self.assertIs(child.change_alters_data_method.alters_data, False)
|
|
|
|
|
|
|
|
grand_child = GrandChild()
|
|
|
|
self.assertIs(grand_child.my_method.alters_data, True)
|
|
|
|
self.assertIs(grand_child.other_method.alters_data, True)
|
|
|
|
self.assertIs(grand_child.change_alters_data_method.alters_data, False)
|
|
|
|
|
|
|
|
c = Context({"element": grand_child})
|
|
|
|
|
|
|
|
t = self.engine.from_string("{{ element.my_method }}")
|
|
|
|
self.assertEqual(t.render(c), "")
|
|
|
|
t = self.engine.from_string("{{ element.other_method }}")
|
|
|
|
self.assertEqual(t.render(c), "")
|
|
|
|
t = self.engine.from_string("{{ element.change_alters_data_method }}")
|
|
|
|
self.assertEqual(t.render(c), "103")
|
|
|
|
t = self.engine.from_string("{{ element.sub_non_callable_method }}")
|
|
|
|
self.assertEqual(t.render(c), "104")
|
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def test_do_not_call(self):
|
2017-01-19 07:39:46 +00:00
|
|
|
class Doodad:
|
2011-04-19 22:06:19 +00:00
|
|
|
do_not_call_in_templates = True
|
2013-10-22 10:21:07 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def __init__(self, value):
|
|
|
|
self.num_calls = 0
|
|
|
|
self.value = value
|
2013-10-22 10:21:07 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def __call__(self):
|
|
|
|
self.num_calls += 1
|
|
|
|
return {"the_value": self.value}
|
|
|
|
|
|
|
|
my_doodad = Doodad(42)
|
2015-04-11 23:41:45 +00:00
|
|
|
c = Context({"my_doodad": my_doodad})
|
2011-04-19 22:06:19 +00:00
|
|
|
|
|
|
|
# Since ``my_doodad.do_not_call_in_templates`` is True, the template
|
|
|
|
# system will not try to call our doodad. We can access its attributes
|
|
|
|
# as normal, and we don't have access to the dict that it returns when
|
|
|
|
# called.
|
2015-04-11 23:41:45 +00:00
|
|
|
t = self.engine.from_string("{{ my_doodad.value }}")
|
2012-06-07 16:08:47 +00:00
|
|
|
self.assertEqual(t.render(c), "42")
|
2015-04-11 23:41:45 +00:00
|
|
|
t = self.engine.from_string("{{ my_doodad.the_value }}")
|
2012-06-07 16:08:47 +00:00
|
|
|
self.assertEqual(t.render(c), "")
|
2011-04-19 22:06:19 +00:00
|
|
|
|
|
|
|
# Double-check that the object was really never called during the
|
|
|
|
# template rendering.
|
|
|
|
self.assertEqual(my_doodad.num_calls, 0)
|
|
|
|
|
|
|
|
def test_do_not_call_and_alters_data(self):
|
|
|
|
# If we combine ``alters_data`` and ``do_not_call_in_templates``, the
|
|
|
|
# ``alters_data`` attribute will not make any difference in the
|
|
|
|
# template system's behavior.
|
|
|
|
|
2017-01-19 07:39:46 +00:00
|
|
|
class Doodad:
|
2011-04-19 22:06:19 +00:00
|
|
|
do_not_call_in_templates = True
|
|
|
|
alters_data = True
|
2013-10-22 10:21:07 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def __init__(self, value):
|
|
|
|
self.num_calls = 0
|
|
|
|
self.value = value
|
2013-10-22 10:21:07 +00:00
|
|
|
|
2011-04-19 22:06:19 +00:00
|
|
|
def __call__(self):
|
|
|
|
self.num_calls += 1
|
|
|
|
return {"the_value": self.value}
|
|
|
|
|
|
|
|
my_doodad = Doodad(42)
|
2015-04-11 23:41:45 +00:00
|
|
|
c = Context({"my_doodad": my_doodad})
|
2011-04-19 22:06:19 +00:00
|
|
|
|
2015-04-11 23:41:45 +00:00
|
|
|
t = self.engine.from_string("{{ my_doodad.value }}")
|
2012-06-07 16:08:47 +00:00
|
|
|
self.assertEqual(t.render(c), "42")
|
2015-04-11 23:41:45 +00:00
|
|
|
t = self.engine.from_string("{{ my_doodad.the_value }}")
|
2012-06-07 16:08:47 +00:00
|
|
|
self.assertEqual(t.render(c), "")
|
2011-04-19 22:06:19 +00:00
|
|
|
|
|
|
|
# Double-check that the object was really never called during the
|
|
|
|
# template rendering.
|
|
|
|
self.assertEqual(my_doodad.num_calls, 0)
|