app/django/template/defaulttags.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
--- a/app/django/template/defaulttags.py	Tue Oct 14 12:36:55 2008 +0000
+++ b/app/django/template/defaulttags.py	Tue Oct 14 16:00:59 2008 +0000
@@ -39,12 +39,11 @@
 
 class CycleNode(Node):
     def __init__(self, cyclevars, variable_name=None):
-        self.cycle_iter = itertools_cycle(cyclevars)
+        self.cycle_iter = itertools_cycle([Variable(v) for v in cyclevars])
         self.variable_name = variable_name
 
     def render(self, context):
-        value = self.cycle_iter.next()
-        value = Variable(value).resolve(context)
+        value = self.cycle_iter.next().resolve(context)
         if self.variable_name:
             context[self.variable_name] = value
         return value
@@ -158,34 +157,37 @@
         return nodelist.render(context)
 
 class IfChangedNode(Node):
-    def __init__(self, nodelist, *varlist):
-        self.nodelist = nodelist
+    def __init__(self, nodelist_true, nodelist_false, *varlist):
+        self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
         self._last_seen = None
         self._varlist = map(Variable, varlist)
+        self._id = str(id(self))
 
     def render(self, context):
-        if 'forloop' in context and context['forloop']['first']:
+        if 'forloop' in context and self._id not in context['forloop']:
             self._last_seen = None
+            context['forloop'][self._id] = 1
         try:
             if self._varlist:
                 # Consider multiple parameters.  This automatically behaves
                 # like an OR evaluation of the multiple variables.
                 compare_to = [var.resolve(context) for var in self._varlist]
             else:
-                compare_to = self.nodelist.render(context)
+                compare_to = self.nodelist_true.render(context)
         except VariableDoesNotExist:
             compare_to = None
 
-        if  compare_to != self._last_seen:
+        if compare_to != self._last_seen:
             firstloop = (self._last_seen == None)
             self._last_seen = compare_to
             context.push()
             context['ifchanged'] = {'firstloop': firstloop}
-            content = self.nodelist.render(context)
+            content = self.nodelist_true.render(context)
             context.pop()
             return content
-        else:
-            return ''
+        elif self.nodelist_false:
+            return self.nodelist_false.render(context)
+        return ''
 
 class IfEqualNode(Node):
     def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
@@ -349,25 +351,40 @@
         return self.mapping.get(self.tagtype, '')
 
 class URLNode(Node):
-    def __init__(self, view_name, args, kwargs):
+    def __init__(self, view_name, args, kwargs, asvar):
         self.view_name = view_name
         self.args = args
         self.kwargs = kwargs
+        self.asvar = asvar
 
     def render(self, context):
         from django.core.urlresolvers import reverse, NoReverseMatch
         args = [arg.resolve(context) for arg in self.args]
         kwargs = dict([(smart_str(k,'ascii'), v.resolve(context))
                        for k, v in self.kwargs.items()])
+        
+        
+        # Try to look up the URL twice: once given the view name, and again
+        # relative to what we guess is the "main" app. If they both fail, 
+        # re-raise the NoReverseMatch unless we're using the 
+        # {% url ... as var %} construct in which cause return nothing.
+        url = ''
         try:
-            return reverse(self.view_name, args=args, kwargs=kwargs)
+            url = reverse(self.view_name, args=args, kwargs=kwargs)
         except NoReverseMatch:
+            project_name = settings.SETTINGS_MODULE.split('.')[0]
             try:
-                project_name = settings.SETTINGS_MODULE.split('.')[0]
-                return reverse(project_name + '.' + self.view_name,
-                               args=args, kwargs=kwargs)
+                url = reverse(project_name + '.' + self.view_name,
+                              args=args, kwargs=kwargs)
             except NoReverseMatch:
-                return ''
+                if self.asvar is None:
+                    raise
+                    
+        if self.asvar:
+            context[self.asvar] = url
+            return ''
+        else:
+            return url
 
 class WidthRatioNode(Node):
     def __init__(self, val_expr, max_expr, max_width):
@@ -452,17 +469,17 @@
             <tr class="{% cycle rowcolors %}">...</tr>
             <tr class="{% cycle rowcolors %}">...</tr>
 
-    You can use any number of values, seperated by spaces. Commas can also
+    You can use any number of values, separated by spaces. Commas can also
     be used to separate values; if a comma is used, the cycle values are
     interpreted as literal strings.
     """
 
     # Note: This returns the exact same node on each {% cycle name %} call;
     # that is, the node object returned from {% cycle a b c as name %} and the
-    # one returned from {% cycle name %} are the exact same object.  This
+    # one returned from {% cycle name %} are the exact same object. This
     # shouldn't cause problems (heh), but if it does, now you know.
     #
-    # Ugly hack warning: this stuffs the named template dict into parser so
+    # Ugly hack warning: This stuffs the named template dict into parser so
     # that names are only unique within each template (as opposed to using
     # a global variable, which would make cycle names have to be unique across
     # *all* templates.
@@ -481,8 +498,7 @@
         # {% cycle foo %} case.
         name = args[1]
         if not hasattr(parser, '_namedCycleNodes'):
-            raise TemplateSyntaxError("No named cycles in template."
-                                      " '%s' is not defined" % name)
+            raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name)
         if not name in parser._namedCycleNodes:
             raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
         return parser._namedCycleNodes[name]
@@ -682,8 +698,10 @@
 def do_if(parser, token):
     """
     The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
-    (i.e. exists, is not empty, and is not a false boolean value) the contents
-    of the block are output::
+    (i.e., exists, is not empty, and is not a false boolean value), the
+    contents of the block are output:
+
+    ::
 
         {% if athlete_list %}
             Number of athletes: {{ athlete_list|count }}
@@ -801,9 +819,14 @@
             {% endfor %}
     """
     bits = token.contents.split()
-    nodelist = parser.parse(('endifchanged',))
-    parser.delete_first_token()
-    return IfChangedNode(nodelist, *bits[1:])
+    nodelist_true = parser.parse(('else', 'endifchanged'))
+    token = parser.next_token()
+    if token.contents == 'else':
+        nodelist_false = parser.parse(('endifchanged',))
+        parser.delete_first_token()
+    else:
+        nodelist_false = NodeList()
+    return IfChangedNode(nodelist_true, nodelist_false, *bits[1:])
 ifchanged = register.tag(ifchanged)
 
 #@register.tag
@@ -1036,21 +1059,30 @@
 
     The URL will look like ``/clients/client/123/``.
     """
-    bits = token.contents.split(' ', 2)
+    bits = token.contents.split(' ')
     if len(bits) < 2:
         raise TemplateSyntaxError("'%s' takes at least one argument"
                                   " (path to a view)" % bits[0])
+    viewname = bits[1]
     args = []
     kwargs = {}
+    asvar = None
+        
     if len(bits) > 2:
-        for arg in bits[2].split(','):
-            if '=' in arg:
-                k, v = arg.split('=', 1)
-                k = k.strip()
-                kwargs[k] = parser.compile_filter(v)
+        bits = iter(bits[2:])
+        for bit in bits:
+            if bit == 'as':
+                asvar = bits.next()
+                break
             else:
-                args.append(parser.compile_filter(arg))
-    return URLNode(bits[1], args, kwargs)
+                for arg in bit.split(","):
+                    if '=' in arg:
+                        k, v = arg.split('=', 1)
+                        k = k.strip()
+                        kwargs[k] = parser.compile_filter(v)
+                    elif arg:
+                        args.append(parser.compile_filter(arg))
+    return URLNode(viewname, args, kwargs, asvar)
 url = register.tag(url)
 
 #@register.tag