Skip to content

Commit 8274fa6

Browse files
onjinbmispelon
authored andcommitted
Made the new template.Context.flatten() method a public API.
That method was introduced in 9db4271. Refs #21765.
1 parent 57ba5bf commit 8274fa6

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
lines changed

django/template/context.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,23 @@ def new(self, values=None):
9797
new_context._reset_dicts(values)
9898
return new_context
9999

100+
def flatten(self):
101+
"""
102+
Returns self.dicts as one dictionary
103+
"""
104+
flat = {}
105+
for d in self.dicts:
106+
flat.update(d)
107+
return flat
108+
100109
def __eq__(self, other):
101110
"""
102111
Compares two contexts by comparing theirs 'dicts' attributes.
103112
"""
104113
if isinstance(other, BaseContext):
105114
# because dictionaries can be put in different order
106115
# we have to flatten them like in templates
107-
def flatten(dicts):
108-
flat = {}
109-
for d in dicts:
110-
flat.update(d)
111-
return flat
112-
113-
return flatten(self.dicts) == flatten(other.dicts)
116+
return self.flatten() == other.flatten()
114117

115118
# if it's not comparable return false
116119
return False

docs/ref/templates/api.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,43 @@ the stack instead of an empty one.
368368
Using a ``Context`` as a stack comes in handy in some custom template tags, as
369369
you'll see below.
370370

371+
.. method:: Context.flatten()
372+
373+
.. versionadded:: 1.7
374+
375+
Using ``flatten()`` method you can get whole ``Context`` stack as one dictionary
376+
including builtin variables.
377+
378+
>>> c = Context()
379+
>>> c['foo'] = 'first level'
380+
>>> c.update({'bar': 'second level'})
381+
{'bar': 'second level'}
382+
>>> c.flatten()
383+
{'True': True, 'None': None, 'foo': 'first level', 'False': False, 'bar': 'second level'}
384+
385+
A ``flatten()`` method is also internally used to make ``Context`` objects comparable.
386+
387+
>>> c1 = Context()
388+
>>> c1['foo'] = 'first level'
389+
>>> c1['bar'] = 'second level'
390+
>>> c2 = Context()
391+
>>> c2.update({'bar': 'second level', 'foo': 'first level'})
392+
{'foo': 'first level', 'bar': 'second level'}
393+
>>> c1 == c2
394+
True
395+
396+
Result from ``flatten()`` can be useful in unit tests to compare ``Context``
397+
against ``dict``::
398+
399+
class ContextTest(unittest.TestCase):
400+
def test_against_dictionary(self):
401+
c1 = Context()
402+
c1['update'] = 'value'
403+
self.assertEqual(c1.flatten(), {
404+
'True': True, 'None': None, 'False': False,
405+
'update': 'value'})
406+
407+
371408
.. _subclassing-context-requestcontext:
372409

373410
Subclassing Context: RequestContext

docs/releases/1.7.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,14 @@ Templates
636636
parameters that are passed to the ``dict`` constructor used to build the new
637637
context level.
638638

639+
* The new :meth:`Context.flatten() <django.template.Context.flatten>` method
640+
returns a ``Context``'s stack as one flat dictionary.
641+
642+
* ``Context`` objects can now be compared for equality (internally, this
643+
uses :meth:`Context.flatten() <django.template.Context.flatten>` so the
644+
internal structure of each ``Context``'s stack doesn't matter as long as their
645+
flattened version is identical).
646+
639647
* The :ttag:`widthratio` template tag now accepts an "as" parameter to capture
640648
the result in a variable.
641649

tests/template_tests/test_context.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,17 @@ def test_render_context(self):
5050
test_context['fruit']
5151
self.assertIsNone(test_context.get('fruit'))
5252

53+
def test_flatten_context(self):
54+
a = Context()
55+
a.update({'a': 2})
56+
a.update({'b': 4})
57+
a.update({'c': 8})
58+
59+
self.assertEqual(a.flatten(), {
60+
'False': False, 'None': None, 'True': True,
61+
'a': 2, 'b': 4, 'c': 8
62+
})
63+
5364
def test_context_comparable(self):
5465
test_data = {'x': 'y', 'v': 'z', 'd': {'o': object, 'a': 'b'}}
5566

0 commit comments

Comments
 (0)