23đź‘Ť
Unfortunately, this is not possible with the default Django template engine. You’ll have to write something ugly like this to emulate a switch.
{% if a %}
{{ a }}
{% else %}
{% if b %}
{{ b }}
{% else %}
{% if c %}
{{ c }}
{% else %}
{{ default }}
{% endif %}
{% endif %}
{% endif %}
or if only one if condition can be true and you don’t need a default.
{% if a %}
{{ a }}
{% endif %}
{% if b %}
{{ b }}
{% endif %}
{% if c %}
{{ c }}
{% endif %}
Usually, when the template engine is not powerful enough to accomplish what you want this is a sign that the code should be moved into Django view instead of in the template. For example:
# Django view
if a:
val = a
elif b:
val = b
elif c:
val = c
else:
val = default
# Template
{{ val }}
58đź‘Ť
As of Django 1.4, there is {% elif %}
:
{% if a %}
thing
{% elif b %}
other thing
{% elif c %}
another thing
{% endif %}
- [Django]-How to iterate over nested dictionaries in django templates
- [Django]-What is the best AJAX library for Django?
- [Django]-Best practice for Python & Django constants
23đź‘Ť
To the previous responders: Without understanding the use case, you’ve made assumptions and criticized the questioner. @Ber says “all over the place” which is certainly not implied by the questioner. Not fair.
I have a case where I would like to do a {% switch %}
statement in exactly one place in my Django template. Not only is it not convenient to move the equivalent of the switch statement into Python code, but that would actually make both the view and the template harder to read and take simple conditional logic that belongs in one place and split it into two places.
In many cases where I could imagine a {% switch %}
(or an {% if %}
) being useful, not using one requires putting HTML in a view. That’s a far worse sin and is why {% if %}
exists in the first place. {% switch %}
is no different.
Fortunately, Django is extensible and multiple people have implemented switch. Check out:
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 ""
- [Django]-Error: No module named psycopg2.extensions
- [Django]-Which database engine to choose for Django app?
- [Django]-Django's SuspiciousOperation Invalid HTTP_HOST header
2đź‘Ť
In a very general view, the need for a switch statement is a sign that there is a need to create new classes and objects that capture the different “cases”.
Then, instead of “swtich”ing all over the place, you only need to call an object method or reference an object attribute and your done.
- [Django]-Getting model ContentType in migration – Django 1.7
- [Django]-Pip install -r requirements.txt [Errno 2] No such file or directory: 'requirements.txt'
- [Django]-How to expire Django session in 5minutes?
0đź‘Ť
This is a fixed version of the code Roy wrote:
@register.tag(name="case")
def case_tag(parser, token):
pass
@register.tag(name="switch")
def switch_tag(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
# TODO: error if there is anything here!
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.split_contents()
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
assert token_name == 'else'
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):
value_missing = False
value = self.variable.resolve(context, True)
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)
assert False, f'No case hit for value {value}'
tests to verify:
import pytest
from django.template import (
Context,
Template,
)
def test_switch():
assert Template('''
{% load base %}
{% switch foo %}
{% case 1 %}
foo
{% endswitch %}
''').render(Context(dict(foo=1))).strip() == 'foo'
assert Template('''
{% load base %}
{% switch foo %}
{% case 1 %}
foo
{% else %}
bar
{% endswitch %}
''').render(Context(dict(foo=2))).strip() == 'bar'
assert Template('''
{% load base %}
{% switch foo %}
{% case 1 %}
foo
{% case 2 %}
bar
{% endswitch %}
''').render(Context(dict(foo=2))).strip() == 'bar'
assert Template('''
{% load base %}
{% switch foo %}
{% case "hello world" %}
foo
{% case "good bye world" %}
bar
{% endswitch %}
''').render(Context(dict(foo="hello world"))).strip() == 'foo'
with pytest.raises(AssertionError):
Template('''
{% load base %}
{% switch foo %}
{% case 1 %}
foo
{% endswitch %}
''').render(Context(dict(foo=0)))
with pytest.raises(AssertionError):
Template('''
{% load base %}
{% switch foo %}
{% case "hello world" %}
foo
{% case "good bye world" %}
bar
{% endswitch %}
''').render(Context(dict(foo="something else"))).strip()
- [Django]-How to access array elements in a Django template?
- [Django]-How to use Django ImageField, and why use it at all?
- [Django]-Tutorial about how to write custom form fields in django?