Hey Ned,

On Sat, 2007-12-29 at 08:27 +1100, Malcolm Tredinnick wrote:
[...]
>         def render(self, context):
>            if context[self.varname] == id(context['forloop']):
>               return self.nodelist_false.render(context)
>            context[self.varname] = id(context['forloop'])
>            return self.nodelist_true.render(context)

This guess is rubbish. The problem is that the context['forloop']
dictionary is a new instance for every iteration of the for-loop, so
there's nothing constant about that outer structure we can use.

After a bit of sleep, some food and some time spent thinking about this
whilst doing other stuff today, there's a better solution (a.k.a one
that works) attached to this mail. I've even tested this one.

During any for-loop, the only thing that remains constant is the
"parentloop" dictionary. That dictionary is created anew every time the
*parent* loop steps forwards, but it is referred to by reference for
each iteration of the inner loop. So we can stick a marker into the
parent loop's data structure (even when there's no parent loop, that
data structure is an empty dictionary).

So, this implementation does depend a bit on the internals of the
ForNode, but that isn't a complete crime. Plus it's been hard to come up
with an alternative. Every time I thought of some tricky data structure
that seemed to do the trick, I was able to break it. A template fragment
such as

        {% for x in data %}
           {% if x %}
              {% iffirst %} yes {% else %} no {% endfirst %}
           {% else %} fail {% endif %}
        {% endfor %}
        
given data = (0, 1, 1, 0) twice in succession (without recompiling), or
giving data = (0, 1, 1, 0) and data = (0, 0, 1, 0) on successive runs
(again, without recompiling) was enough to break most of my attempts.

It's also not too hard to extend this to be check whether this is the
first time around the parent loop using a similar method, although
nested "iffirst" tags seems hard.

Best wishes,
Malcolm

-- 
Tolkien is hobbit-forming. 
http://www.pointy-stick.com/blog/

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

from django.template import Node, NodeList, TemplateSyntaxError, Library

class IfFirstNode(Node):
    def __init__(self, nodelist_true, nodelist_false):
        self.nodelist_true = nodelist_true
        self.nodelist_false = nodelist_false

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

    def render(self, context):
        if '_first' in context['forloop']['parentloop']:
            return self.nodelist_false.render(context)
        context['forloop']['parentloop']['_first'] = True
        return self.nodelist_true.render(context)

register = Library()

@register.tag
def iffirst(parser, token):
    bits = token.split_contents()
    if len(bits) != 1:
        raise TemplateSyntaxError("%r takes no arguments." % bits[0])
    nodelist_true = parser.parse(('else', 'endiffirst'))
    token = parser.next_token()
    if token.contents == 'else':
        nodelist_false= parser.parse(('endiffirst',))
        parser.delete_first_token()
    else:
        nodelist_false = NodeList()
    return IfFirstNode(nodelist_true, nodelist_false)

Reply via email to