diff --git a/docs/usage.rst b/docs/usage.rst index edfead5e9..00f31b362 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -33,7 +33,10 @@ You can switch it on in `pytest.ini`:: [pytest] FAIL_INVALID_TEMPLATE_VARS = True - + +Invalid template variables will not fail the test if the variable uses the Django +`default` filter, like `{{ does_not_exist:default:"ok" }}`. + Additional pytest.ini settings ------------------------------ diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 290b8b49d..b8b8d3f39 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -551,6 +551,17 @@ def __contains__(self, key): def _get_origin(): stack = inspect.stack() + # Don't flag non-existent variables with default filter applied. + from django.template.defaultfilters import default as default_filter + try: + has_default_filter = any( + filter[0] is default_filter + for filter in stack[2][0].f_locals["self"].filters) + except (AttributeError, IndexError, KeyError): + has_default_filter = False + if has_default_filter: + return True, None + # Try to use topmost `self.origin` first (Django 1.9+, and with # TEMPLATE_DEBUG).. for f in stack[2:]: @@ -562,7 +573,7 @@ def _get_origin(): except (AttributeError, KeyError): continue if origin is not None: - return origin + return False, origin from django.template import Template @@ -579,10 +590,14 @@ def _get_origin(): # ``django.template.base.Template`` template = f_locals["self"] if isinstance(template, Template): - return template.name + return False, template.name + + return False, None def __mod__(self, var): - origin = self._get_origin() + has_default_filter, origin = self._get_origin() + if has_default_filter: + return "" if origin: msg = "Undefined template variable '{}' in '{}'".format(var, origin) else: diff --git a/tests/test_environment.py b/tests/test_environment.py index 87e45f5ff..d21b61cf2 100644 --- a/tests/test_environment.py +++ b/tests/test_environment.py @@ -112,11 +112,44 @@ def test_ignore(client): ROOT_URLCONF = 'tpkg.app.urls' """ ) -def test_invalid_template_with_default_if_none(django_testdir): +def test_invalid_template_with_default(django_testdir): django_testdir.create_app_file( """
{{ data.empty|default:'d' }}
{{ data.none|default:'d' }}
+
{{ data.missing|default:'d' }}
+ """, + "templates/the_template.html", + ) + django_testdir.create_test_module( + """ + def test_for_invalid_template(): + from django.shortcuts import render + + + render( + request=None, + template_name='the_template.html', + context={'data': {'empty': '', 'none': None}}, + ) + """ + ) + result = django_testdir.runpytest_subprocess("--fail-on-template-vars") + result.stdout.fnmatch_lines_random(["tpkg/test_the_test.py ."]) + + +@pytest.mark.django_project( + extra_settings=""" + TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + ) + ROOT_URLCONF = 'tpkg.app.urls' + """ +) +def test_invalid_template_with_default_if_none(django_testdir): + django_testdir.create_app_file( + """
{{ data.empty|default_if_none:'d' }}
{{ data.none|default_if_none:'d' }}
{{ data.missing|default_if_none:'d' }}