Unexpected clearing of a context dictionary while rendering a template

This test:

from django.template import Template, Context
from django.test import TestCase

class TemplateTestCase(TestCase):
    def test_template(self):
        t = Template('''
        {% for p in parts %}
        {{ p.name }} is {{ p.color }} and {% if p.clear %}clear{% else %}not clear{% endif %}.
        {% endfor %}
        ''')
        context = {
            'parts': [
                {
                    'name': 'one',
                    'color': 'red',
                },
                {
                    'name': 'two',
                    'color': 'green',
                    'clear': False
                },
                {
                    'name': 'three',
                    'color': 'blue',
                }
            ]
        }
        s1 = t.render(Context(context)).strip()
        s2 = t.render(Context(context)).strip()
        self.assertEqual(s1, s2)

fails, perhaps unexpectedly. The problem is caused by the key clear not being present in the passed context in some of the dictionaries, with the {% if p.clear %} clearing the dictionary represented by p because its dict.clear() method is called. I know the ability to access a no-argument method result is useful in templates, but is it documented that certain keys, like clear, are a no-go-area? Or is this a bug which requires extra checks in Django to avoid calling dict.clear(), list.clear() and the like?

Hi @vsajip.

Yes, that’s just how variable lookup works…

You should either change keys or use a simple filter in this kind of case.

Sure, I’ve even used that call-if-callable feature before, but when porting an application over, I hit this inadvertently. Of course I’ve worked around it, but I wonder if the documentation you linked to could be improved with specific mention of clear - the example of unexpected things happening refers to defaultdict.items, which is perhaps a less common scenario than using plain dicts and lists.

How about {% for key, value in parts.items %}?

then you can do:
{% if key == 'clear' %}

Thanks, in this case it’s not convenient to enumerate the items. But I worked around it by changing the key used from clear to is_clear in the data.

2 Likes