1
0
mirror of https://github.com/django/django.git synced 2025-10-24 14:16:09 +00:00

[soc2009/multidb] Merged up to trunk r11864.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11866 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor
2009-12-14 17:47:04 +00:00
parent 2794cceb5f
commit 4e25ca00c8
45 changed files with 1598 additions and 240 deletions

View File

@@ -463,6 +463,85 @@ new ``Context`` in this example, the results would have *always* been
automatically escaped, which may not be the desired behavior if the template
tag is used inside a ``{% autoescape off %}`` block.
.. _template_tag_thread_safety:
Thread-safety considerations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.2
Once a node is parsed, its ``render`` method may be called any number of times.
Since Django is sometimes run in multi-threaded environments, a single node may
be simultaneously rendering with different contexts in response to two separate
requests. Therefore, it's important to make sure your template tags are thread
safe.
To make sure your template tags are thread safe, you should never store state
information on the node itself. For example, Django provides a builtin ``cycle``
template tag that cycles among a list of given strings each time it's rendered::
{% for o in some_list %}
<tr class="{% cycle 'row1' 'row2' %}>
...
</tr>
{% endfor %}
A naive implementation of ``CycleNode`` might look something like this::
class CycleNode(Node):
def __init__(self, cyclevars):
self.cycle_iter = itertools.cycle(cyclevars)
def render(self, context):
return self.cycle_iter.next()
But, suppose we have two templates rendering the template snippet from above at
the same time:
1. Thread 1 performs its first loop iteration, ``CycleNode.render()``
returns 'row1'
2. Thread 2 performs its first loop iteration, ``CycleNode.render()``
returns 'row2'
3. Thread 1 performs its second loop iteration, ``CycleNode.render()``
returns 'row1'
4. Thread 2 performs its second loop iteration, ``CycleNode.render()``
returns 'row2'
The CycleNode is iterating, but it's iterating globally. As far as Thread 1
and Thread 2 are concerned, it's always returning the same value. This is
obviously not what we want!
To address this problem, Django provides a ``render_context`` that's associated
with the ``context`` of the template that is currently being rendered. The
``render_context`` behaves like a Python dictionary, and should be used to store
``Node`` state between invocations of the ``render`` method.
Let's refactor our ``CycleNode`` implementation to use the ``render_context``::
class CycleNode(Node):
def __init__(self, cyclevars):
self.cyclevars = cyclevars
def render(self, context):
if self not in context.render_context:
context.render_context[self] = itertools.cycle(self.cyclevars)
cycle_iter = context.render_context[self]
return cycle_iter.next()
Note that it's perfectly safe to store global information that will not change
throughout the life of the ``Node`` as an attribute. In the case of
``CycleNode``, the ``cyclevars`` argument doesn't change after the ``Node`` is
instantiated, so we don't need to put it in the ``render_context``. But state
information that is specific to the template that is currently being rendered,
like the current iteration of the ``CycleNode``, should be stored in the
``render_context``.
.. note::
Notice how we used ``self`` to scope the ``CycleNode`` specific information
within the ``render_context``. There may be multiple ``CycleNodes`` in a
given template, so we need to be careful not to clobber another node's state
information. The easiest way to do this is to always use ``self`` as the key
into ``render_context``. If you're keeping track of several state variables,
make ``render_context[self]`` a dictionary.
Registering the tag
~~~~~~~~~~~~~~~~~~~