2009-04-07 6 views
22

Znalazłem link, aby mieć znacznik "przełącznika" w szablonach Django, ale zastanawiałem się, czy można to w jakiś sposób osiągnąć bez niego. Korzystasz tylko z rzeczy dostarczanych z Django? Zasadniczo istnieje inny sposób, a następnie użycie wielu instrukcji "if" lub "ifequal"?Jak uzyskać funkcjonalność instrukcji "switch-case" w szablonach Django?

Z góry dziękuję za wszelkie wskazówki/sugestie.

+0

+1 Dzięki za linka, ale mówią, że szablony nie jest dla 'programowanie' i logiki biznesowej –

Odpowiedz

21

Niestety, nie jest to możliwe z domyślnym silnikiem szablonów Django. Będziesz musiał napisać coś tak brzydkiego, aby emulować przełącznik.

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

lub jeśli tylko jeden warunek może być prawdziwy, a użytkownik nie potrzebuje wartości domyślnej.

{% if a %} 
{{ a }} 
{% endif %} 
{% if b %} 
{{ b }} 
{% endif %} 
{% if c %} 
{{ c }} 
{% endif %} 

Zazwyczaj, gdy silnik szablon nie jest wystarczająco silne, aby osiągnąć to, co chcesz, to jest to znak, że kod powinien zostać przeniesiony do widoku Django zamiast w szablonie. Na przykład:

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

# Template 
{{ val }} 
+10

Od wersji Django 1.4, elif jest obsługiwany –

+1

Zobacz filtr szablonu 'firstof', który to skrót. Nie jestem pewien, kiedy został wprowadzony. – chris

2

W bardzo ogólnym zdaniem, potrzeba instrukcji switch jest to znak, że istnieje potrzeba stworzenia nowych klas i obiektów, które uchwycić różne „przypadki”.

Następnie, zamiast "wymieniać" w całym miejscu, wystarczy wywołać metodę obiektową lub odwołać atrybut obiektu i gotowe.

+0

Ma sens w języku OOP, ale nie w języku szablonów Django. Nie możesz/nie powinieneś "wywoływać metody obiektu" w celu renderowania w szablonie Django. –

16

Do poprzednich respondentów: Bez zrozumienia przypadku użycia, przyjęliście założenia i skrytykowali pytającego. @Ber mówi "w każdym miejscu", co z pewnością nie jest implikowane przez pytającego. Niesprawiedliwe.

Mam przypadek, w którym chciałbym wykonać oświadczenie {% switch %} w dokładnie jednym miejscu w moim szablonie Django. Nie tylko nie jest wygodne przenoszenie ekwiwalentu instrukcji switch na kod Pythona, ale to spowodowałoby, że zarówno widok, jak i szablon byłyby trudniejsze do odczytania i podjęcia prostej logiki warunkowej, która należy do jednego miejsca i podzielona na dwa miejsca.

W wielu przypadkach, w których mogłem sobie wyobrazić, że {% switch %} (lub {% if %}) jest użyteczne, nieużywanie wymaga umieszczenia HTML w widoku. To o wiele gorszy grzech i dlatego w pierwszej kolejności istnieje {% if %}. {% switch %} nie jest inaczej.

Na szczęście Django można rozszerzyć i wiele osób wdrożyło przełącznik. Sprawdź:

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 "" 
+0

To jest genialne! Myślę, że moim jedynym dodatkiem byłoby uczynienie go tak, abyś mógł podać 'with x as y' i przekazać go do każdego przypadku. Ale to jest wymóg niszowy. – Pureferret

41

Jak Django 1.4, istnieje {% elif %}:

{% if a %} 
    thing 
{% elif b %} 
    other thing 
{% elif c %} 
    another thing 
{% endif %} 
+2

W połączeniu ze znacznikiem "z" możemy uzyskać całkiem niezły dźwięk zbliżony do przełącznika. '{% with a = ya.expensive.bro%} {% if a = 1%} jeden {% elif a = 2%} dwa {% else%} uhh {% endif%} {% endwith%}' .. To brzydki dotyk. –