I was having an issue where no matter what I did I couldn't access an IntEnum (from the enum34 lib) in my Django template.
I was able to get around it by converting it to a dict:
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
# Django templates don't play nice with Enums
context['DEMOS'] = {d.name: d for d in DEMOS}
# `context['DEMOS'] = DEMOS` doesn't work
return context
These don't work when DEMO is an IntEnum, but do when DEMO is converted to a dict:
{{ DEMO.FOO }} # outputs nothing
{{ DEMO.FOO|default_if_none:'foo' }} # outputs nothing
{{ DEMO.FOO.value }} # outputs nothing
{% if DEMO.FOO == 1 %} # no matter what I compare to, always False
Any ideas why? Is this a known issue?
Django Template Engine provides filters which are used to transform the values of variables;es and tag arguments. We have already discussed major Django Template Tags. Tags can't modify value of a variable whereas filters can be used for incrementing value of a variable or modifying it to one's own need.
Enumeration types Enum member values are a tuple of arguments to use when constructing the concrete data type. Django supports adding an extra string value to the end of this tuple to be used as the human-readable name, or label . The label can be a lazy translatable string.
{% %} is basically used when you have an expression and are called tags while {{ }} is used to simply access the variable.
The include tag allows you include a template inside the current template. This is useful when you have a block of content that are the same for many pages.
A little more digging and I found the answer.
From Django's templates documentation:
Technically, when the template system encounters a dot, it tries the following lookups, in this order:
Dictionary lookup
Attribute or method lookup
Numeric index lookup
If the resulting value is callable, it is called with no arguments. The result of the call becomes the template value.
That last line should say:
If any of the resulting/intermediate values is callable, ...
Stepping through that process:
lookup 'DEMOS'
in the context
, get <enum 'DEMOS'>
check if it is callable (it is)
call it with no arguments
get a TypeError
So the problem is that an Enum
class is callable, and the templating system will try to call it, which will raise an error and abort (returning an empty string: ''
).
However, there is a way around that problem.
django.templates.base
's calling code contains the following
guard condition:
if getattr(current, 'do_not_call_in_templates', False):
pass
The code checks for an attribute called do_not_call_in_templates
, and if True
then it will skip the call portion, which should solve the problem.
Using Python's Enum
(or the enum34
backport) the simplest way will be to use a decorator. If Django doesn't already have one for this purpose, you can roll your own easily enough:
def forDjango(cls):
cls.do_not_call_in_templates = True
return cls
and then decorate your Enum
:
@forDjango
class DEMOS(Enum):
eggs = 'runny'
spam = 'hard'
cheese = 'smelly'
The additional attribute does not interfere with your set of Enum constants, because Enums are fixed once defined.
Using Django's Enum
you can use __members__
property:
context['DEMOS'] = DEMOS.__members__
Reference
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With