Proper way of checking variable's existing in templates

What is the recommended way of checking the existence of a variable in a template?

Assuming this minimal example:

# context in views.py
context = {'var_one': True, 'var_three': False}
# template.html
{% if var_one %}{{ var_one }}{% endif %}
{% if var_two %}{{ var_two }}{% endif %}
{% if var_three %}{{ var_three }}{% endif %} 

The output is as expected:

True

But running this in production writes following line into my log file:

raise VariableDoesNotExist(django.template.base.VariableDoesNotExist: Failed lookup for key [var_two] in [...]

The following settings are used:

# part of settings.py
DEBUG = False

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/var/log/debug_django.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

Iā€™m aware that I could predefine every possible variable in context to avoid that. But Iā€™m reusing a lot of templates by even more views whereby the variables are used as switch to either show content or not (load the content related template with include to be more specific). Making any change on a template (e.g. adding a switch) would cause a lot of work (preset added switch in any view using this template).

Further online reading let me guess, that Iā€™m using a ā€œgoodā€ way by checking variable in template with if-condition but how to avoid the raised ValueDoesNotExist error?

<opinion>
One of the real strengths and benefits of the Django template language is that you donā€™t need to do that in the general case.

Some people disagree, and I can appreciate their concerns.

But I have found that the ability to not provide every variable in a context allows for more generic and flexible templates to be used, reducing both the size and number of templates needed for a project.
<opinion>

If you find those messages bothersome, create a filter for your logging configuration to filter them out. (The logger would be ā€˜django.templateā€™.)

But these types of log messages should be considered ā€œroutineā€ or ā€œnormalā€ under most circumstances.

1 Like

Thanks for pointing out that this is an intentional behaviour. Itā€™s really annoying because this behaviour extremely bloats up my debug files due to the way Iā€™m checking for existence. For the moment I increased the logging level for ā€˜django.templateā€™ to WARNING, but I consider adding/creating a tag to explicitly check existence of variables in templates. With this I could keep working in DRY principles and having an explicit check which would be the most pythonic way I think.

# extended part of LOGGING in settings.py
# [...]
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
        'django.template': {	# avoid log of missing context variables as DEBUG messages
            'handlers': ['file'],
            'level': 'WARNING',
            'propagate': False,
        },
    },
# [...]

I share your opinion about the flexibility of templates by not providing every possible variable in a context and I was expecting that there must be another built-in way to check for existence than manipulating logging.

Why?

You have two different objectives here.

First, you have what I consider to be the common case - if a template variable does not exist in the context, render nothing. However, you may also have the case where a variable that doesnā€™t exist could be an indication of a problem, and so you may want that information logged.

<opinion>
Determining whether you want that information kept, what is to be logged, and where and how it is to be stored seems to me to be exactly the type of thing the logging configuration is designed to handle.

I canā€™t think of a better or more suitable place for it.
</opinion>

Side note, there is a difference between:

and
{{ var_one }}
in that the if condition will not render a ā€œFalsyā€ value where the second version will.

I was thinking, as you stated, that checking a context variable for existence is a common case and it might be implemented in django. Well, it is implemented to be precise but itā€™s ever throwing errors at DEBUG level when a variable does not exist and I was expecting that there is a more explicit way than filtering logs or pushing up log level.

Get me right, this is neither an offense nor a complain. I was just wondering if there is a built-in way to check for existence in templates without having internally thrown errors. But of course django cannot catch every use case or every favor. Iā€™m able to implement this for myself. In this case itā€™s not that much work (at least implementing the tag, altering the templates may take some time, but this is based on my design decision how I use templates and is nothing anyone who contributed to django is responsible for).

Yes, Iā€™m aware of it. Thatā€™s why I mentioned that it would be a solution (but not for me) to set all possible context variables to None or False which are used as switch to show content. Errors are thrown in both cases (when checked in a condition and when directly used), of course.

First, to not lose track of the primary question:

Sorry, no.

<conjecture>
Iā€™m going to guess that not raising the exception could cause other problems within the rendering engine. (Iā€™m thinking in terms of how the parsed template nodes are handled, and the potential interrelationship between those variables and other chained references and/or filters.) It seems possible to me that having something like {{ var.invalid.something_else }} is just going to throw a different exception if invalid does not exist - and the processing of that node isnā€™t short-circuited at that point.
</conjecture>

Agreed. At this point Iā€™m thinking about this as a ā€œthought experimentā€ or an ā€œacademic exerciseā€. i.e., What would happen if the resolver didnā€™t throw an exception at that location, ?

100% spot-on here. You can see where this is done in django.template.base.Variable._resolve_lookup method.

In theory, you do have a couple different ways to work around this. Implementing a custom tag (e.g., {% var %} ?) might be one way to do it. (You could even create a custom loader that parsed the {{ .. }} identifier and caused that to be interpreted as {% ... %} instead.) Another would be to monkey-patch the Variable._resolve_lookup method.

Itā€™s just that anything along those lines seems like a lot more work, with uncertain side-effects, than configuring the logger.

After learning that there is no built-in way, the easiest and most predictable way for me seems to use a simple_tag like

# custom_tags.py, NOT TESTED
@register.simple_tag(takes_context=True)
def exists(context, key):
    return key in context
# template.html, NOT TESTED
{% exists 'var' as var_exists %}
{% if var_exists %}
    {{ var }}
{% endif %}

I did not tested it so maybe itā€™s not working. But every other way (monkey patching, custom loader, ā€¦) may have side effects which Iā€™m not aware of.

It would be better when this would be usable like

# abstract code, NOT WORKING
{% if var|exists %}
    {{ var }}
{% endif %}

But I didnā€™t take much effort to check how to get context into a filter or if this is even possible.

I agree but configuring the logger is a quite implicit way of avoidance. For me itā€™s more clear when I can see already in the template that a variable might not exist, even this is only my personal favor.