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

yvt65v4c  于 2023-10-21  发布在  Go
关注(0)|答案(5)|浏览(155)

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

fdbelqdn

fdbelqdn1#

从Django 1.4开始,有{% elif %}

  1. {% if a %}
  2. thing
  3. {% elif b %}
  4. other thing
  5. {% elif c %}
  6. another thing
  7. {% endif %}
yyyllmsg

yyyllmsg2#

不幸的是,这在默认的Django模板引擎中是不可能的。您必须编写类似这样的丑陋代码来模拟开关。

  1. {% if a %}
  2. {{ a }}
  3. {% else %}
  4. {% if b %}
  5. {{ b }}
  6. {% else %}
  7. {% if c %}
  8. {{ c }}
  9. {% else %}
  10. {{ default }}
  11. {% endif %}
  12. {% endif %}
  13. {% endif %}

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

  1. {% if a %}
  2. {{ a }}
  3. {% endif %}
  4. {% if b %}
  5. {{ b }}
  6. {% endif %}
  7. {% if c %}
  8. {{ c }}
  9. {% endif %}

通常,当模板引擎不足以完成你想要的任务时,这是一个信号,表明代码应该被移到Django视图中,而不是模板中。举例来说:

  1. # Django view
  2. if a:
  3. val = a
  4. elif b:
  5. val = b
  6. elif c:
  7. val = c
  8. else:
  9. val = default
  10. # Template
  11. {{ val }}
展开查看全部
up9lanfz

up9lanfz3#

对于以前的回应者:在不理解用例的情况下,你就做出了假设并批评了提问者。@Ber说“到处都是”,这当然不是提问者所暗示的。不公平
我有一个例子,我想在Django模板中的一个地方执行{% switch %}语句。将switch语句的等价物移到Python代码中不仅不方便,而且实际上会使视图和模板更难阅读,并将属于一个地方的简单条件逻辑分成两个地方。
在许多情况下,我可以想象{% switch %}(或{% if %})是有用的,不使用一个需要在视图中放置HTML。这是一个更糟糕的罪过,这就是为什么{% if %}首先存在。{% switch %}没有什么不同。
幸运的是,Django是可扩展的,很多人已经实现了switch。退房时间:
Switch template tag

  1. from django import template
  2. from django.template import Library, Node, VariableDoesNotExist
  3. register = Library()
  4. @register.tag(name="switch")
  5. def do_switch(parser, token):
  6. """
  7. The ``{% switch %}`` tag compares a variable against one or more values in
  8. ``{% case %}`` tags, and outputs the contents of the matching block. An
  9. optional ``{% else %}`` tag sets off the default output if no matches
  10. could be found::
  11. {% switch result_count %}
  12. {% case 0 %}
  13. There are no search results.
  14. {% case 1 %}
  15. There is one search result.
  16. {% else %}
  17. Jackpot! Your search found {{ result_count }} results.
  18. {% endswitch %}
  19. Each ``{% case %}`` tag can take multiple values to compare the variable
  20. against::
  21. {% switch username %}
  22. {% case "Jim" "Bob" "Joe" %}
  23. Me old mate {{ username }}! How ya doin?
  24. {% else %}
  25. Hello {{ username }}
  26. {% endswitch %}
  27. """
  28. bits = token.contents.split()
  29. tag_name = bits[0]
  30. if len(bits) != 2:
  31. raise template.TemplateSyntaxError("'%s' tag requires one argument" % tag_name)
  32. variable = parser.compile_filter(bits[1])
  33. class BlockTagList(object):
  34. # This is a bit of a hack, as it embeds knowledge of the behaviour
  35. # of Parser.parse() relating to the "parse_until" argument.
  36. def __init__(self, *names):
  37. self.names = set(names)
  38. def __contains__(self, token_contents):
  39. name = token_contents.split()[0]
  40. return name in self.names
  41. # Skip over everything before the first {% case %} tag
  42. parser.parse(BlockTagList('case', 'endswitch'))
  43. cases = []
  44. token = parser.next_token()
  45. got_case = False
  46. got_else = False
  47. while token.contents != 'endswitch':
  48. nodelist = parser.parse(BlockTagList('case', 'else', 'endswitch'))
  49. if got_else:
  50. raise template.TemplateSyntaxError("'else' must be last tag in '%s'." % tag_name)
  51. contents = token.contents.split()
  52. token_name, token_args = contents[0], contents[1:]
  53. if token_name == 'case':
  54. tests = map(parser.compile_filter, token_args)
  55. case = (tests, nodelist)
  56. got_case = True
  57. else:
  58. # The {% else %} tag
  59. case = (None, nodelist)
  60. got_else = True
  61. cases.append(case)
  62. token = parser.next_token()
  63. if not got_case:
  64. raise template.TemplateSyntaxError("'%s' must have at least one 'case'." % tag_name)
  65. return SwitchNode(variable, cases)
  66. class SwitchNode(Node):
  67. def __init__(self, variable, cases):
  68. self.variable = variable
  69. self.cases = cases
  70. def __repr__(self):
  71. return "<Switch node>"
  72. def __iter__(self):
  73. for tests, nodelist in self.cases:
  74. for node in nodelist:
  75. yield node
  76. def get_nodes_by_type(self, nodetype):
  77. nodes = []
  78. if isinstance(self, nodetype):
  79. nodes.append(self)
  80. for tests, nodelist in self.cases:
  81. nodes.extend(nodelist.get_nodes_by_type(nodetype))
  82. return nodes
  83. def render(self, context):
  84. try:
  85. value_missing = False
  86. value = self.variable.resolve(context, True)
  87. except VariableDoesNotExist:
  88. no_value = True
  89. value_missing = None
  90. for tests, nodelist in self.cases:
  91. if tests is None:
  92. return nodelist.render(context)
  93. elif not value_missing:
  94. for test in tests:
  95. test_value = test.resolve(context, True)
  96. if value == test_value:
  97. return nodelist.render(context)
  98. else:
  99. return ""
展开查看全部
htzpubme

htzpubme4#

在一个非常普遍的观点中,对switch语句的需要是一个信号,表明需要创建新的类和对象来捕获不同的“情况”。
然后,你只需要调用一个对象方法或引用一个对象属性,而不是到处“切换“。

n1bvdmb6

n1bvdmb65#

这是Roy写的代码的固定版本:

  1. @register.tag(name="case")
  2. def case_tag(parser, token):
  3. pass
  4. @register.tag(name="switch")
  5. def switch_tag(parser, token):
  6. """
  7. The ``{% switch %}`` tag compares a variable against one or more values in
  8. ``{% case %}`` tags, and outputs the contents of the matching block. An
  9. optional ``{% else %}`` tag sets off the default output if no matches
  10. could be found::
  11. {% switch result_count %}
  12. {% case 0 %}
  13. There are no search results.
  14. {% case 1 %}
  15. There is one search result.
  16. {% else %}
  17. Jackpot! Your search found {{ result_count }} results.
  18. {% endswitch %}
  19. Each ``{% case %}`` tag can take multiple values to compare the variable
  20. against::
  21. {% switch username %}
  22. {% case "Jim" "Bob" "Joe" %}
  23. Me old mate {{ username }}! How ya doin?
  24. {% else %}
  25. Hello {{ username }}
  26. {% endswitch %}
  27. """
  28. bits = token.contents.split()
  29. tag_name = bits[0]
  30. if len(bits) != 2:
  31. raise template.TemplateSyntaxError("'%s' tag requires one argument" % tag_name)
  32. variable = parser.compile_filter(bits[1])
  33. class BlockTagList(object):
  34. # This is a bit of a hack, as it embeds knowledge of the behaviour
  35. # of Parser.parse() relating to the "parse_until" argument.
  36. def __init__(self, *names):
  37. self.names = set(names)
  38. def __contains__(self, token_contents):
  39. name = token_contents.split()[0]
  40. return name in self.names
  41. # Skip over everything before the first {% case %} tag
  42. # TODO: error if there is anything here!
  43. parser.parse(BlockTagList('case', 'endswitch'))
  44. cases = []
  45. token = parser.next_token()
  46. got_case = False
  47. got_else = False
  48. while token.contents != 'endswitch':
  49. nodelist = parser.parse(BlockTagList('case', 'else', 'endswitch'))
  50. if got_else:
  51. raise template.TemplateSyntaxError("'else' must be last tag in '%s'." % tag_name)
  52. contents = token.split_contents()
  53. token_name, token_args = contents[0], contents[1:]
  54. if token_name == 'case':
  55. tests = map(parser.compile_filter, token_args)
  56. case = (tests, nodelist)
  57. got_case = True
  58. else:
  59. # The {% else %} tag
  60. assert token_name == 'else'
  61. case = (None, nodelist)
  62. got_else = True
  63. cases.append(case)
  64. token = parser.next_token()
  65. if not got_case:
  66. raise template.TemplateSyntaxError("'%s' must have at least one 'case'." % tag_name)
  67. return SwitchNode(variable, cases)
  68. class SwitchNode(Node):
  69. def __init__(self, variable, cases):
  70. self.variable = variable
  71. self.cases = cases
  72. def __repr__(self):
  73. return "<Switch node>"
  74. def __iter__(self):
  75. for tests, nodelist in self.cases:
  76. for node in nodelist:
  77. yield node
  78. def get_nodes_by_type(self, nodetype):
  79. nodes = []
  80. if isinstance(self, nodetype):
  81. nodes.append(self)
  82. for tests, nodelist in self.cases:
  83. nodes.extend(nodelist.get_nodes_by_type(nodetype))
  84. return nodes
  85. def render(self, context):
  86. value_missing = False
  87. value = self.variable.resolve(context, True)
  88. for tests, nodelist in self.cases:
  89. if tests is None:
  90. return nodelist.render(context)
  91. elif not value_missing:
  92. for test in tests:
  93. test_value = test.resolve(context, True)
  94. if value == test_value:
  95. return nodelist.render(context)
  96. assert False, f'No case hit for value {value}'

测试验证:

  1. import pytest
  2. from django.template import (
  3. Context,
  4. Template,
  5. )
  6. def test_switch():
  7. assert Template('''
  8. {% load base %}
  9. {% switch foo %}
  10. {% case 1 %}
  11. foo
  12. {% endswitch %}
  13. ''').render(Context(dict(foo=1))).strip() == 'foo'
  14. assert Template('''
  15. {% load base %}
  16. {% switch foo %}
  17. {% case 1 %}
  18. foo
  19. {% else %}
  20. bar
  21. {% endswitch %}
  22. ''').render(Context(dict(foo=2))).strip() == 'bar'
  23. assert Template('''
  24. {% load base %}
  25. {% switch foo %}
  26. {% case 1 %}
  27. foo
  28. {% case 2 %}
  29. bar
  30. {% endswitch %}
  31. ''').render(Context(dict(foo=2))).strip() == 'bar'
  32. assert Template('''
  33. {% load base %}
  34. {% switch foo %}
  35. {% case "hello world" %}
  36. foo
  37. {% case "good bye world" %}
  38. bar
  39. {% endswitch %}
  40. ''').render(Context(dict(foo="hello world"))).strip() == 'foo'
  41. with pytest.raises(AssertionError):
  42. Template('''
  43. {% load base %}
  44. {% switch foo %}
  45. {% case 1 %}
  46. foo
  47. {% endswitch %}
  48. ''').render(Context(dict(foo=0)))
  49. with pytest.raises(AssertionError):
  50. Template('''
  51. {% load base %}
  52. {% switch foo %}
  53. {% case "hello world" %}
  54. foo
  55. {% case "good bye world" %}
  56. bar
  57. {% endswitch %}
  58. ''').render(Context(dict(foo="something else"))).strip()
展开查看全部

相关问题