A quick follow up on this post.  I forgot to explain how to avoid the
problem.  To avoid the problem is to treat self as read only.

So to fix the ReprNode, you'd simply do the following:

    class ReprNode(template.Node):
        def __init__(self, obj_expression)
            # Let's name this more accurately
            self.obj_expression = obj_expression

        def render(self, context):
            # Use a local variable instead of a object attribute
            obj = template.resolve_variable(self.obj_expression,
context)
            return str(obj)

On Jun 17, 6:14 pm, Eric <[EMAIL PROTECTED]> wrote:
> This isn't a bug but it's something that might need to be documented
> to prevent folks from doing something that may hurt them later:
>
> In the Django template system.  There is a small caveat that you need
> to
> recognize when developing your own template tags.
>
> When Django parses the Node tree it creates a template.Node instance
> for
> each template tag in the template.  The node tree is just like our
> beloved
> HTML DOM.
>
> So for instance take the following::
>
>     <ul>
>       <li>1</li>
>       <li>2</li>
>       <li>3</li>
>       <li>4</li>
>       <li>5</li>
>     </ul>
>
> That turns into something like this::
>
>     <ul:Element>
>       <li:HTMLElement>
>       <li:HTMLElement>
>       <li:HTMLElement>
>       <li:HTMLElement>
>       <li:HTMLElement>
>
> The indention shows that the li's are child nodes of the ul tag. Each
> li in a
> different instance of an HTMLElement, each with their own state.
>
> So for a Django Template take the following::
>
>     {% block content %}
>       {% firstof var1 var2 var3 %}
>       {% firstof var1 var2 var3 %}
>       {% firstof var1 var2 var3 %}
>       {% firstof var1 var2 var3 %}
>     {% endblock %}
>
> The node tree would look like this::
>
>     <BlockNode>
>       <FirstOfNode>
>       <FirstOfNode>
>       <FirstOfNode>
>       <FirstOfNode>
>
> So we have a block node with four FirstOfNode instances
>
> In python this would translate roughly to::
>
>     f1 = FirstOfNode(...)
>     f2 = FirstOfNode(...)
>     f3 = FirstOfNode(...)
>     f4 = FirstOfNode(...)
>
>     f1.render()
>     f2.render()
>     f3.render()
>     f4.render()
>
> Everything looks fine here.  Render is called once per node instance.
>
> Now, check this out::
>
>     {% block content %}
>       {% for i in value_list %} In the Django template system.  There
> is a small caveat that you need to
> recognize when developing your own template tags.
>
> When Django parses the Node tree it creates a template.Node instance
> for
> each template tag in the template.  The node tree is just like our
> beloved
> HTML DOM.
>
> So for instance take the following::
>
>     <ul>
>       <li>1</li>
>       <li>2</li>
>       <li>3</li>
>       <li>4</li>
>       <li>5</li>
>     </ul>
>
> That turns into something like this::
>
>     <ul:Element>
>       <li:HTMLElement>
>       <li:HTMLElement>
>       <li:HTMLElement>
>       <li:HTMLElement>
>       <li:HTMLElement>
>
> The indention shows that the li's are child nodes of the ul tag. Each
> li in a
> different instance of an HTMLElement, each with their own state.
>
> So for a Django Template take the following::
>
>     {% block content %}
>       {% firstof var1 var2 var3 %}
>       {% firstof var1 var2 var3 %}
>       {% firstof var1 var2 var3 %}
>       {% firstof var1 var2 var3 %}
>     {% endblock %}
>
> The node tree would look like this::
>
>     <BlockNode>
>       <FirstOfNode>
>       <FirstOfNode>
>       <FirstOfNode>
>       <FirstOfNode>
>
> So we have a block node with four FirstOfNode instances
>
> In python this would translate roughly to::
>
>     f1 = FirstOfNode(...)
>     f2 = FirstOfNode(...)
>     f3 = FirstOfNode(...)
>     f4 = FirstOfNode(...)
>
>     f1.render()
>     f2.render()
>     f3.render()
>     f4.render()
>
> Everything looks fine here.  Render is called once per node instance.
>
> Now, check this out::
>
>     {% block content %}
>       {% for i in value_list %}
>         {% repr obj %}
>       {% endfor %}
>     {% endblock %}
>
> The node tree that django builds would look something like this::
>
>     <BlockNode>
>       <ForNode>
>         <ReprNode>
>
> Now each node in that tree is it's own object instance with it's own
> state.  This can be an issue if you don't realize one thing, each node
> is persistent while the template is rendering.
>
> Let me write out how the template rendering of the a loop would look
> in python with the ReprNode::
>
>     repr_node = ReprNode("obj")
>
>     for i in range(10):
>         repr_node.render(context)
>
> Can you tell what the problem?  We're calling the render method
> on the same instance of the Node.
>
> Let's peek under the covers and look at ReprNode's definition::
>
>     class ReprNode(template.Node):
>         def __init__(self, obj)
>             self.obj = obj
>
>         def render(self, context):
>             self.obj = template.resolve_variable(self.obj, context)
>             return str(self.obj)
>
>         def do_repr(parser, token):
>             tag_name, obj = token.split_contents()
>             return ReprNode(obj)
>
> Now look at what's going on.  self.obj is assumed to be the name of
> the
> variable in the context.  That's fine when render is called once.
> When render
> called a second time, self.obj is now the actual value of obj from the
> context.
> What happens when you try to resolve that?  You get a
> VariableDoesNotExist
> error, uh oh!
>
> The assumption that render() is only called once.  You don't realize
> that inside a for tag, the render method is called repeatedly.  This
> can cause issues if you assume render will only be called once.
>
> I don't know if this is technically a bug in the Django templating
> system,
> bad design, bad documentation, or just simply poor assumptions on my
> part.
>         {% repr obj %}
>       {% endfor %}
>     {% endblock %}
>
> The node tree that django builds would look something like this::
>
>     <BlockNode>
>       <ForNode>
>         <ReprNode>
>
> Now each node in that tree is it's own object instance with it's own
> state.  This can be an issue if you don't realize one thing, each node
> is persistent while the template is rendering.
>
> Let me write out how the template rendering of the a loop would look
> in python with the ReprNode::
>
>     repr_node = ReprNode("obj")
>
>     for i in range(10):
>         repr_node.render(context)
>
> Can you tell what the problem?  We're calling the render method
> on the same instance of the Node.
>
> Let's peek under the covers and look at ReprNode's definition::
>
>     class ReprNode(template.Node):
>         def __init__(self, obj)
>             self.obj = obj
>
>         def render(self, context):
>             self.obj = template.resolve_variable(self.obj, context)
>             return str(self.obj)
>
>         def do_repr(parser, token):
>             tag_name, obj = token.split_contents()
>             return ReprNode(obj)
>
> Now look at what's going on.  self.obj is assumed to be the name of
> the
> variable in the context.  That's fine when render is called once.
> When render
> called a second time, self.obj is now the actual value of obj from the
> context.
> What happens when you try to resolve that?  You get a
> VariableDoesNotExist
> error, uh oh!
>
> I made the assumption that render() is only called once.  You don't
> realize that inside a for tag, the render method is called
> repeatedly.  This can cause tons of issues.  If it wasn't a huge
> design change, TemplateTag should probably be Static classes.
>
> I don't know if this is technically a bug in the Django templating
> system,
> bad design, bad documentation, or just simply poor assumptions on my
> part, but I came across this issue today and it took stepping through
> PDB to find the problem with my custom template tag (the ReprNode was
> completely made up for the example)
>
> Example code <http://www.djangosnippets.org/snippets/811/>
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to [email protected]
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
-~----------~----~----~----~------~----~------~--~---

Reply via email to