Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #17348 -- Implemented {% elif %}. Refs #3100.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17187 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 19cbdf8c8f6f5da5687cfec659841176b6af7d67 1 parent 959f78b
Aymeric Augustin authored December 09, 2011
77  django/template/defaulttags.py
@@ -250,31 +250,37 @@ def render(self, context):
250 250
         return self.nodelist_false.render(context)
251 251
 
252 252
 class IfNode(Node):
253  
-    child_nodelists = ('nodelist_true', 'nodelist_false')
254 253
 
255  
-    def __init__(self, var, nodelist_true, nodelist_false=None):
256  
-        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
257  
-        self.var = var
  254
+    def __init__(self, conditions_nodelists):
  255
+        self.conditions_nodelists = conditions_nodelists
258 256
 
259 257
     def __repr__(self):
260  
-        return "<If node>"
  258
+        return "<IfNode>"
261 259
 
262 260
     def __iter__(self):
263  
-        for node in self.nodelist_true:
264  
-            yield node
265  
-        for node in self.nodelist_false:
266  
-            yield node
  261
+        for _, nodelist in self.conditions_nodelists:
  262
+            for node in nodelist:
  263
+                yield node
  264
+
  265
+    @property
  266
+    def nodelist(self):
  267
+        return NodeList(node for _, nodelist in self.conditions_nodelists for node in nodelist)
267 268
 
268 269
     def render(self, context):
269  
-        try:
270  
-            var = self.var.eval(context)
271  
-        except VariableDoesNotExist:
272  
-            var = None
  270
+        for condition, nodelist in self.conditions_nodelists:
273 271
 
274  
-        if var:
275  
-            return self.nodelist_true.render(context)
276  
-        else:
277  
-            return self.nodelist_false.render(context)
  272
+            if condition is not None:           # if / elif clause
  273
+                try:
  274
+                    match = condition.eval(context)
  275
+                except VariableDoesNotExist:
  276
+                    match = None
  277
+            else:                               # else clause
  278
+                match = True
  279
+
  280
+            if match:
  281
+                return nodelist.render(context)
  282
+
  283
+        return ''
278 284
 
279 285
 class RegroupNode(Node):
280 286
     def __init__(self, target, expression, var_name):
@@ -825,6 +831,8 @@ def do_if(parser, token):
825 831
 
826 832
         {% if athlete_list %}
827 833
             Number of athletes: {{ athlete_list|count }}
  834
+        {% elif athlete_in_locker_room_list %}
  835
+            Athletes should be out of the locker room soon!
828 836
         {% else %}
829 837
             No athletes.
830 838
         {% endif %}
@@ -832,8 +840,9 @@ def do_if(parser, token):
832 840
     In the above, if ``athlete_list`` is not empty, the number of athletes will
833 841
     be displayed by the ``{{ athlete_list|count }}`` variable.
834 842
 
835  
-    As you can see, the ``if`` tag can take an option ``{% else %}`` clause
836  
-    that will be displayed if the test fails.
  843
+    As you can see, the ``if`` tag may take one or several `` {% elif %}``
  844
+    clauses, as well as an ``{% else %}`` clause that will be displayed if all
  845
+    previous conditions fail. These clauses are optional.
837 846
 
838 847
     ``if`` tags may use ``or``, ``and`` or ``not`` to test a number of
839 848
     variables or to negate a given variable::
@@ -871,16 +880,32 @@ def do_if(parser, token):
871 880
 
872 881
     Operator precedence follows Python.
873 882
     """
  883
+    # {% if ... %}
874 884
     bits = token.split_contents()[1:]
875  
-    var = TemplateIfParser(parser, bits).parse()
876  
-    nodelist_true = parser.parse(('else', 'endif'))
  885
+    condition = TemplateIfParser(parser, bits).parse()
  886
+    nodelist = parser.parse(('elif', 'else', 'endif'))
  887
+    conditions_nodelists = [(condition, nodelist)]
877 888
     token = parser.next_token()
  889
+
  890
+    # {% elif ... %} (repeatable)
  891
+    while token.contents.startswith('elif'):
  892
+        bits = token.split_contents()[1:]
  893
+        condition = TemplateIfParser(parser, bits).parse()
  894
+        nodelist = parser.parse(('elif', 'else', 'endif'))
  895
+        conditions_nodelists.append((condition, nodelist))
  896
+        token = parser.next_token()
  897
+
  898
+    # {% else %} (optional)
878 899
     if token.contents == 'else':
879  
-        nodelist_false = parser.parse(('endif',))
880  
-        parser.delete_first_token()
881  
-    else:
882  
-        nodelist_false = NodeList()
883  
-    return IfNode(var, nodelist_true, nodelist_false)
  900
+        nodelist = parser.parse(('endif',))
  901
+        conditions_nodelists.append((None, nodelist))
  902
+        token = parser.next_token()
  903
+
  904
+    # {% endif %}
  905
+    assert token.contents == 'endif'
  906
+
  907
+    return IfNode(conditions_nodelists)
  908
+
884 909
 
885 910
 @register.tag
886 911
 def ifchanged(parser, token):
11  docs/ref/templates/builtins.txt
@@ -366,6 +366,8 @@ block are output::
366 366
 
367 367
     {% if athlete_list %}
368 368
         Number of athletes: {{ athlete_list|length }}
  369
+    {% elif athlete_in_locker_room_list %}
  370
+        Athletes should be out of the locker room soon!
369 371
     {% else %}
370 372
         No athletes.
371 373
     {% endif %}
@@ -373,8 +375,13 @@ block are output::
373 375
 In the above, if ``athlete_list`` is not empty, the number of athletes will be
374 376
 displayed by the ``{{ athlete_list|length }}`` variable.
375 377
 
376  
-As you can see, the ``if`` tag can take an optional ``{% else %}`` clause that
377  
-will be displayed if the test fails.
  378
+As you can see, the ``if`` tag may take one or several `` {% elif %}``
  379
+clauses, as well as an ``{% else %}`` clause that will be displayed if all
  380
+previous conditions fail. These clauses are optional.
  381
+
  382
+.. versionadded:: 1.4
  383
+
  384
+The ``if`` tag now supports ``{% elif %}`` clauses.
378 385
 
379 386
 Boolean operators
380 387
 ^^^^^^^^^^^^^^^^^
2  docs/releases/1.4.txt
@@ -484,6 +484,8 @@ Django 1.4 also includes several smaller improvements worth noting:
484 484
   be able to retrieve a translation string without displaying it but setting
485 485
   a template context variable instead.
486 486
 
  487
+* The :ttag:`if` template tag now supports ``{% elif %}`` clauses.
  488
+
487 489
 * A new plain text version of the HTTP 500 status code internal error page
488 490
   served when :setting:`DEBUG` is ``True`` is now sent to the client when
489 491
   Django detects that the request has originated in JavaScript code
13  tests/regressiontests/templates/tests.py
@@ -394,7 +394,7 @@ def test_invalid_block_suggestion(self):
394 394
         try:
395 395
             t = Template("{% if 1 %}lala{% endblock %}{% endif %}")
396 396
         except TemplateSyntaxError, e:
397  
-            self.assertEqual(e.args[0], "Invalid block tag: 'endblock', expected 'else' or 'endif'")
  397
+            self.assertEqual(e.args[0], "Invalid block tag: 'endblock', expected 'elif', 'else' or 'endif'")
398 398
 
399 399
     def test_templates(self):
400 400
         template_tests = self.get_template_tests()
@@ -823,6 +823,17 @@ def get_template_tests(self):
823 823
             'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
824 824
             'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"),
825 825
 
  826
+            'if-tag04': ("{% if foo %}foo{% elif bar %}bar{% endif %}", {'foo': True}, "foo"),
  827
+            'if-tag05': ("{% if foo %}foo{% elif bar %}bar{% endif %}", {'bar': True}, "bar"),
  828
+            'if-tag06': ("{% if foo %}foo{% elif bar %}bar{% endif %}", {}, ""),
  829
+            'if-tag07': ("{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}", {'foo': True}, "foo"),
  830
+            'if-tag08': ("{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}", {'bar': True}, "bar"),
  831
+            'if-tag09': ("{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}", {}, "nothing"),
  832
+            'if-tag10': ("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}", {'foo': True}, "foo"),
  833
+            'if-tag11': ("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}", {'bar': True}, "bar"),
  834
+            'if-tag12': ("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}", {'baz': True}, "baz"),
  835
+            'if-tag13': ("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}", {}, "nothing"),
  836
+
826 837
             # Filters
827 838
             'if-tag-filter01': ("{% if foo|length == 5 %}yes{% else %}no{% endif %}", {'foo': 'abcde'}, "yes"),
828 839
             'if-tag-filter02': ("{% if foo|upper == 'ABC' %}yes{% else %}no{% endif %}", {}, "no"),

0 notes on commit 19cbdf8

Please sign in to comment.
Something went wrong with that request. Please try again.