2013-06-11 26 views
5

Piszę szablon django i chcę rozróżnić między egzystencji zmiennej kontekstowej w stosunku do tego, że jest Brak, pusty itp. Odrobiłem swoją pracę domową i wydaje się to zaskakująco trudne. W szczególności, jest to, co usiłuję zrobićdjango sprawdź istnienie zmiennej kontekstowej szablonu

view 1: 
... 
if some_condition = True: 
    context['letters'] = ['a', 'b', 'c'] # The list might also be empty or None in some cases 
else 
    context['numbers'] = [1, 2, 3] #This list might be empty or None in some cases 

Template 
... 
<ul> 
{% if letters %} 
    {% for x in letter %} 
     <li>{{x}}</li> 
    {%endfor%} 
{% else %} 
    {%for x in numbers%} 
     <li>{{x}}</li> 
    {%endfor%} 
</ul> 

Używanie {% if %} jest ryzykowny, bo to się nie powiedzie, jeśli letters robi istnieje lub lista jest pusta. Chcę używać letters nawet jeśli jest pusta (ale zdefiniowane w kontekście)

Mam ten sam problem z wbudowanych filtrów default i default_if_none Jak mogę odróżnić istnienie zmiennej kontekstowego ze to jest inna rzeczy takie jak None lub Empty

Odpowiedz

4

Niedawno stanął przed samą zagadką, a po spojrzeniu w sposób tag {% if %} jest zorganizowany oto co wymyśliłem:

from django.template.base import VariableDoesNotExist 
from django.template.defaulttags import IfNode 
from django.template.smartif import IfParser, Literal 

# Used as a value for ifdef and ifndef tags 
undefined = object() 

class IfDefLiteral(Literal): 
    def eval(self, context): 
     if not self.value in context: 
      # Can't raise an exception here because Operator catches it 
      return undefined 

class IfDefParser(IfParser): 
    def create_var(self, value): 
     return IfDefLiteral(value) 

class IfDefNode(IfNode): 
    def __init__(self, defined=True, *args, **kwargs): 
     self.defined = defined 
     super(IfDefNode, self).__init__(*args, **kwargs) 

    def __repr__(self): 
     return "<%s>" % self.__class__.__name__ 

    def render(self, context): 
     for condition, nodelist in self.conditions_nodelists: 

      match = undefined 
      if condition is not None:   # if/elif clause 
       try: 
        match = condition.eval(context) 
       except VariableDoesNotExist: 
        pass 

      if condition is None or ( # else clause, always render 
       (self.defined and match is not undefined) or 
       (match is undefined and not self.defined)): 
       return nodelist.render(context) 

     return '' 

def _gen_ifdef(parser, token, block_tokens, defined): 
    # {% if ... %} 
    bits = token.split_contents()[1:] 
    condition = IfDefParser(bits).parse() 
    nodelist = parser.parse(block_tokens) 
    conditions_nodelists = [(condition, nodelist)] 
    token = parser.next_token() 

    # {% elif ... %} (repeatable) 
    while token.contents.startswith(block_tokens[0]): 
     bits = token.split_contents()[1:] 
     condition = IfDefParser(bits).parse() 
     nodelist = parser.parse(block_tokens) 
     conditions_nodelists.append((condition, nodelist)) 
     token = parser.next_token() 

    # {% else %} (optional) 
    if token.contents == 'else': 
     nodelist = parser.parse(block_tokens[-1:]) 
     conditions_nodelists.append((None, nodelist)) 
     token = parser.next_token() 

    # {% endif %} 
    assert token.contents == block_tokens[-1] 

    return IfDefNode(defined, conditions_nodelists) 

@register.tag 
def ifdef(parser, token): 
    """Check if variable is defined in the context 

    Unlike the {% if %} tag, this renders the block if the variable(s) 
    exist within the context, not only if they are truthy. That is, variables 
    with None, 0 or [] values would also render the block. 
    """ 
    return _gen_ifdef(parser, token, ('elifdef', 'else', 'endifdef'), True) 

@register.tag 
def ifndef(parser, token): 
    """Check if variable is *not* defined in the context 

    This is the opposite of {% ifdef %}. 
    """ 
    return _gen_ifdef(parser, token, ('elifndef', 'else', 'endifndef'), False) 

Wtedy go używać jak {% if %} tag w szablonie:

{% ifdef letters or numbers %} 
    {# do something with letters or numbers #} 
{% else %} 
    {# variables are not defined here #} 
{% endifdef %} 

Nie jestem pewien, czy jest prostszy sposób, aby to osiągnąć, i chociaż nie jestem zbyt zadowolony z podejścia, wydaje się, że działa dobrze w moim przypadku użycia. Mam nadzieję że to pomoże!

+0

dziękuję! Sądzę, że to najczystsze z wielu nieczystych rozwiązań. – sha

0

Nie jestem pewien, czy tego rodzaju logika powinna być używana w szablonach, powinny być proste. Sposób Chciałbym rozwiązać to jest po prostu dodać (wewnątrz związku, przed „jeśli”):

context['letters'] = False #([]/None/False) 
if some_condition = True: 
    ... 

teraz jeśli some_condition = False, niż „za” pętla nie będzie działać w szablonie, więc nie trzeba "jeśli" już tam jest.