如何在 Django 模板中获得“switch-case”语句功能?

Posted

技术标签:

【中文标题】如何在 Django 模板中获得“switch-case”语句功能?【英文标题】:How to get 'switch-case' statement functionality in Django templates? 【发布时间】:2010-10-18 01:37:20 【问题描述】:

我发现link 在 Django 模板中有一个“switch”标签,但我想知道如果没有它是否可以以某种方式实现。只使用 Django 自带的东西?基本上除了使用多个'if'或'ifequal'语句还有其他方法吗?

提前感谢任何提示/建议。

【问题讨论】:

+1 感谢您的链接,但他们说模板不适用于“编程”和业务逻辑 【参考方案1】:

从 Django 1.4 开始,有 % elif %:

% if a %
  thing
% elif b %
  other thing
% elif c %
  another thing
% endif % 

【讨论】:

当与'with'标签结合使用时,我们可以非常接近开关。 % with a=ya.expensive.bro % % if a=1 % one % elif a=2 % two % else % uhh % endif % % endwith % ... 只是有点丑。【参考方案2】:

致之前的回复者:在不了解用例的情况下,您做出了假设并批评了提问者。 @Ber 说“到处都是”,提问者当然没有暗示。不公平。

我有一个案例,我想在我的 Django 模板中的一个位置执行 % switch % 语句。将 switch 语句的等效项移动到 Python 代码中不仅不方便,而且实际上会使视图和模板更难阅读,并采用属于一个地方的简单条件逻辑并将其分成两个地方。

在许多情况下,我可以想象% switch %(或% if %)很有用,不使用需要将 html 放入视图中。这是更严重的罪过,这就是为什么% if % 首先存在的原因。 % switch % 也不例外。

幸运的是,Django 是可扩展的,并且已经多人实现了 switch。签出:

Switch template tag

from django import template
from django.template import Library, Node, VariableDoesNotExist

register = Library()


@register.tag(name="switch")
def do_switch(parser, token):
    """
    The ``% switch %`` tag compares a variable against one or more values in
    ``% case %`` tags, and outputs the contents of the matching block.  An
    optional ``% else %`` tag sets off the default output if no matches
    could be found::

        % switch result_count %
            % case 0 %
                There are no search results.
            % case 1 %
                There is one search result.
            % else %
                Jackpot! Your search found  result_count  results.
        % endswitch %

    Each ``% case %`` tag can take multiple values to compare the variable
    against::

        % switch username %
            % case "Jim" "Bob" "Joe" %
                Me old mate  username ! How ya doin?
            % else %
                Hello  username 
        % endswitch %
    """
    bits = token.contents.split()
    tag_name = bits[0]
    if len(bits) != 2:
        raise template.TemplateSyntaxError("'%s' tag requires one argument" % tag_name)
    variable = parser.compile_filter(bits[1])

    class BlockTagList(object):
        # This is a bit of a hack, as it embeds knowledge of the behaviour
        # of Parser.parse() relating to the "parse_until" argument.
        def __init__(self, *names):
            self.names = set(names)
        def __contains__(self, token_contents):
            name = token_contents.split()[0]
            return name in self.names

    # Skip over everything before the first % case % tag
    parser.parse(BlockTagList('case', 'endswitch'))

    cases = []
    token = parser.next_token()
    got_case = False
    got_else = False
    while token.contents != 'endswitch':
        nodelist = parser.parse(BlockTagList('case', 'else', 'endswitch'))

        if got_else:
            raise template.TemplateSyntaxError("'else' must be last tag in '%s'." % tag_name)

        contents = token.contents.split()
        token_name, token_args = contents[0], contents[1:]

        if token_name == 'case':
            tests = map(parser.compile_filter, token_args)
            case = (tests, nodelist)
            got_case = True
        else:
            # The % else % tag
            case = (None, nodelist)
            got_else = True
        cases.append(case)
        token = parser.next_token()

    if not got_case:
        raise template.TemplateSyntaxError("'%s' must have at least one 'case'." % tag_name)

    return SwitchNode(variable, cases)

class SwitchNode(Node):
    def __init__(self, variable, cases):
        self.variable = variable
        self.cases = cases

    def __repr__(self):
        return "<Switch node>"

    def __iter__(self):
        for tests, nodelist in self.cases:
            for node in nodelist:
                yield node

    def get_nodes_by_type(self, nodetype):
        nodes = []
        if isinstance(self, nodetype):
            nodes.append(self)
        for tests, nodelist in self.cases:
            nodes.extend(nodelist.get_nodes_by_type(nodetype))
        return nodes

    def render(self, context):
        try:
            value_missing = False
            value = self.variable.resolve(context, True)
        except VariableDoesNotExist:
            no_value = True
            value_missing = None

        for tests, nodelist in self.cases:
            if tests is None:
                return nodelist.render(context)
            elif not value_missing:
                for test in tests:
                    test_value = test.resolve(context, True)
                    if value == test_value:
                        return nodelist.render(context)
        else:
            return ""

【讨论】:

这太棒了!我认为我唯一的补充就是让它可以指定一个with x as y 并将它传递给每个案例。但这是一个利基要求。【参考方案3】:

不幸的是,默认的 Django 模板引擎无法做到这一点。你必须写一些像这样丑陋的东西来模拟一个开关。

% if a %
     a 
% else %
    % if b %
         b 
    % else %
        % if c %
             c 
        % else %
             default 
        % endif %
    % endif %
% endif %

或者如果只有一个 if 条件可以为真并且您不需要默认值。

% if a %
 a 
% endif %
% if b %
 b 
% endif %
% if c %
 c 
% endif %

通常,当模板引擎功能不足以完成您想要的工作时,这表明代码应该被移到 Django 视图而不是模板中。例如:

# Django view
if a:
  val = a
elif b:
  val = b
elif c:
  val = c
else:
  val = default

# Template
 val 

【讨论】:

从 Django 1.4 开始,支持 elif 查看firstof 模板过滤器,它是它的缩写。我不确定它是什么时候推出的。【参考方案4】:

在一个非常普遍的观点中,对 switch 语句的需求表明需要创建新的类和对象来捕获不同的“情况”。

然后,您无需到处“切换”,只需调用对象方法或引用对象属性即可。

【讨论】:

在 OOP 语言中有意义,但在 Django 模板语言中没有意义。您不能/不应该“调用对象方法”在 Django 模板中进行渲染。

以上是关于如何在 Django 模板中获得“switch-case”语句功能?的主要内容,如果未能解决你的问题,请参考以下文章

如何在Django模板中显示数组值?

如何访问 Django 模板中的字典元素?

如何访问 Django 模板中的字典元素?

如何在 Django 中获取服务器名称以获得完整的 url?

将关键字段访问到 Django 模板中

Django 模板中的动态分页