如何在Django模板中访问枚举类型

Seá*_*yes 11 python django enums django-templates

我遇到了一个问题,无论我做什么,我都无法访问我的Django模板中的IntEnum(来自enum34 lib).

通过将其转换为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
Run Code Online (Sandbox Code Playgroud)

当DEMO是IntEnum时这些不起作用,但是当DEMO转换为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
Run Code Online (Sandbox Code Playgroud)

有什么想法吗?这是一个已知的问题?

Eth*_*man 12

多挖一点,我找到了答案.

来自网站:

从技术上讲,当模板系统遇到一个点时,它会按以下顺序尝试以下查找:

字典查找

属性或方法查找

数字索引查找

如果结果值是可调用的,则调用它时不带参数.调用的结果成为模板值.

最后一行应该说:

如果任何结果/中间值可以调用,...

逐步完成该过程:

  • 查找'DEMOS'context,得到<enum 'DEMOS'>

  • 检查它是否可调用(它是)

  • 不带参数调用它

  • 得到 TypeError

所以问题是一个Enum类是可调用的,模板系统会尝试调用它,这将引发错误并中止(返回一个空字符串:) ''.

但是,有一种方法可以解决这个问题. django.templates.base的调用代码包含以下保护条件:

if getattr(current, 'do_not_call_in_templates', False):
    pass
Run Code Online (Sandbox Code Playgroud)

代码检查一个被调用的属性do_not_call_in_templates,如果True那么它将跳过调用部分,这应解决问题.

使用Python Enum(或enum34backport)最简单的方法是使用装饰器.如果Django还没有用于此目的,你可以轻松地自己动手:

def forDjango(cls):
    cls.do_not_call_in_templates = True
    return cls
Run Code Online (Sandbox Code Playgroud)

然后装饰你的Enum:

@forDjango
class DEMOS(Enum):
    eggs = 'runny'
    spam = 'hard'
    cheese = 'smelly'
Run Code Online (Sandbox Code Playgroud)

附加属性不会干扰您的枚举常量集,因为枚举一旦定义就会被修复.


pot*_*ion 6

使用 Django,Enum您可以使用__members__属性:

context['DEMOS'] = DEMOS.__members__
Run Code Online (Sandbox Code Playgroud)

参考