1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
from unittest import TestCase
from django.template import Context, Engine
class CallableVariablesTests(TestCase):
@classmethod
def setUpClass(cls):
cls.engine = Engine()
super(CallableVariablesTests, cls).setUpClass()
def test_callable(self):
class Doodad(object):
def __init__(self, value):
self.num_calls = 0
self.value = value
def __call__(self):
self.num_calls += 1
return {"the_value": self.value}
my_doodad = Doodad(42)
c = Context({"my_doodad": my_doodad})
# 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``.
t = self.engine.from_string('{{ my_doodad.value }}')
self.assertEqual(t.render(c), '')
# 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.
t = self.engine.from_string('{{ my_doodad.the_value }}')
self.assertEqual(t.render(c), '42')
self.assertEqual(my_doodad.num_calls, 2)
def test_alters_data(self):
class Doodad(object):
alters_data = True
def __init__(self, value):
self.num_calls = 0
self.value = value
def __call__(self):
self.num_calls += 1
return {"the_value": self.value}
my_doodad = Doodad(42)
c = Context({"my_doodad": my_doodad})
# Since ``my_doodad.alters_data`` is True, the template system will not
# try to call our doodad but will use string_if_invalid
t = self.engine.from_string('{{ my_doodad.value }}')
self.assertEqual(t.render(c), '')
t = self.engine.from_string('{{ my_doodad.the_value }}')
self.assertEqual(t.render(c), '')
# 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(self):
class Doodad(object):
do_not_call_in_templates = True
def __init__(self, value):
self.num_calls = 0
self.value = value
def __call__(self):
self.num_calls += 1
return {"the_value": self.value}
my_doodad = Doodad(42)
c = Context({"my_doodad": my_doodad})
# 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.
t = self.engine.from_string('{{ my_doodad.value }}')
self.assertEqual(t.render(c), '42')
t = self.engine.from_string('{{ my_doodad.the_value }}')
self.assertEqual(t.render(c), '')
# 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.
class Doodad(object):
do_not_call_in_templates = True
alters_data = True
def __init__(self, value):
self.num_calls = 0
self.value = value
def __call__(self):
self.num_calls += 1
return {"the_value": self.value}
my_doodad = Doodad(42)
c = Context({"my_doodad": my_doodad})
t = self.engine.from_string('{{ my_doodad.value }}')
self.assertEqual(t.render(c), '42')
t = self.engine.from_string('{{ my_doodad.the_value }}')
self.assertEqual(t.render(c), '')
# Double-check that the object was really never called during the
# template rendering.
self.assertEqual(my_doodad.num_calls, 0)
|