Undefined Variables
When rendering a Liquid template, if a variable name can not be resolved, an instance of liquid.Undefined
is used instead. We can customize template rendering behavior by implementing some of Python's "magic" methods on a subclass of liquid.Undefined
.
Default Undefined
All operations on the default Undefined
type are silently ignored and, when rendered, it produces an empty string. For example, you can access properties and iterate an undefined variable without error.
Hello {{ nosuchthing }}
{% for thing in nosuchthing %}
{{ thing }}
{% endfor %}
Hello
Strict Undefined
When liquid.StrictUndefined
is passed as the undefined
argument to Environment
or Template
, any operation on an undefined variable will raise an UndefinedError
.
from liquid import Environment, StrictUndefined
env = Environment(undefined=StrictUndefined)
template = env.from_string("Hello {{ nosuchthing }}")
template.render()
# UndefinedError: 'nosuchthing' is undefined, on line 1
The default filter
With StrictUndefined
, the built-in default
filter does not handle undefined variables the way you might expect. The following example will raise an UndefinedError
if username
is undefined.
Hello {{ username | default: "user" }}
New in version 1.4.0
We can use the built-in StrictDefaultUndefined
type, which plays nicely with the default
filter, while still providing strictness elsewhere.
from liquid import Environment
from liquid import StrictDefaultUndefined
env = Environment(undefined=StrictDefaultUndefined)
template = env.from_string('Hello {{ username | default: "user" }}')
print(template.render())
Hello user
Falsy StrictUndefined
It's usually not possible to detect undefined variables in a template using an if
tag. In Python Liquid we can implement an Undefined
type that allows us to write {% if nosuchthing %}
or {% if nosuchthing == 'foo' %}
, but still get some strictness when undefined variables are used elsewhere.
from liquid import Environment
from liquid import StrictUndefined
class FalsyStrictUndefined(StrictUndefined):
# Properties that don't raise an UndefinedError.
allowed_properties = frozenset(
[
"__repr__",
"__bool__",
"__eq__",
"__liquid__",
"__class__",
"name",
"hint",
"obj",
"msg",
]
)
def __bool__(self) -> bool:
return False
def __eq__(self, other: object) -> bool:
return other is False
env = Environment(undefined=FalsyStrictUndefined)
template = env.from_string("{% if nosuchthing %}foo{% else %}bar{% endif %}")
print(template.render()) # "bar"
template = env.from_string("{% if nosuchthing == 'hi' %}foo{% else %}bar{% endif %}")
print(template.render()) # "bar"
template = env.from_string("{{ nosuchthing | upcase }}")
template.render()
# UndefinedError: 'nosuchthing' is undefined, on line 1