diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py
index 38b2fff..d688983 100644
--- a/django/template/loader_tags.py
+++ b/django/template/loader_tags.py
@@ -1,6 +1,6 @@
 from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
 from django.template import Library, Node, TextNode
-from django.template.loader import get_template, get_template_from_string, find_template_source
+from django.template.loader import get_template
 from django.conf import settings
 from django.utils.safestring import mark_safe
 
@@ -10,91 +10,144 @@ class ExtendsError(Exception):
     pass
 
 class BlockNode(Node):
-    def __init__(self, name, nodelist, parent=None):
-        self.name, self.nodelist, self.parent = name, nodelist, parent
+    def __init__(self, name, nodelist):
+        self.name, self.nodelist = name, nodelist
 
     def __repr__(self):
         return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
 
-    def render(self, context):
-        context.push()
-        # Save context in case of block.super().
-        self.context = context
-        context['block'] = self
-        result = self.nodelist.render(context)
-        context.pop()
+    def render(self, context, force_self=False):
+        """
+        Find the correct BlockNode and render its nodelist.
+        case 1: no extends tags encountered => render self
+        case 2: in block.super => render self
+        case 3: extends tags but not in block.super => render "youngest" version of this block
+        """
+        block_context = context.get(BlockContext.KEY, None)
+        if block_context is None:
+            # no extends tag encountered; block tags have no effect
+            return self.nodelist.render(context)
+        elif force_self:
+            block = self
+        else:
+            block = block_context[self.name]
+
+        block_context.enter(block)
+        result = block.nodelist.render(context)
+        block_context.exit(block)
         return result
 
-    def super(self):
-        if self.parent:
-            return mark_safe(self.parent.render(self.context))
-        return ''
+class BlockContext(object):
+    """
+    BlockContext contains the block definitions for a chain of ExtendsNodes.
+    The BlockContext is dynamically generated during rendering because the
+    extends tag supports setting the parent template dynamically.
+    Unlike template.Context, BlockContext.get(name) returns the "first"
+    version added since that is defined in the youngest ancestor.
+    """
+    KEY = 'block' # so that {{ block.super }} works
+
+    def __init__(self, context):
+        self.context = context
+        self.dicts = []
+        self.block_stack = []
+
+    def __repr__(self):
+        return 'BlockContext(%r, %r)' % (self.block_stack, self.dicts)
+
+    def __getitem__(self, key):
+        "Get a block by name"
+        for d in self.dicts:
+            if key in d:
+                return d[key]
+        raise KeyError(key)
+
+    def add_blocks(self, blocks_dict):
+        "Like Context.update() but puts blocks_dict last."
+        if not hasattr(blocks_dict, '__getitem__'): 
+            raise TypeError('blocks_dict must be a mapping (dictionary-like) object.')
+        self.dicts.append(blocks_dict)
+
+    def enter(self, block):
+        # TODO: check for loops
+        self.block_stack.append(block)
+
+    def exit(self, block):
+        last = self.block_stack.pop()
+        if last is not block:
+            raise ValueError('Invalid block stack: %r' % (self.block_stack + [last]))
+
+    def get_parent(self, block):
+        "Find the parent version of a given block, if any"
+        name = block.name
+        found = False
+        for d in self.dicts:
+            b = d.get(name, None)
+            if b:
+                if found:
+                    return b
+                elif b is block:
+                    found = True
+        return None
 
-    def add_parent(self, nodelist):
-        if self.parent:
-            self.parent.add_parent(nodelist)
+    def super(self):
+        "Implement block.super"
+        parent = self.get_parent(self.block_stack[-1])
+        if parent:
+            return mark_safe(parent.render(self.context, force_self=True))
         else:
-            self.parent = BlockNode(self.name, nodelist)
+            return u''
 
 class ExtendsNode(Node):
     must_be_first = True
 
     def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
         self.nodelist = nodelist
-        self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
-        self.template_dirs = template_dirs
+        self.block_nodes = dict([(n.name, n) for n in nodelist.get_nodes_by_type(BlockNode)])
+        if parent_name:
+            try:
+                self.parent = get_template(parent_name)
+            except TemplateDoesNotExist:
+                error_msg = "Invalid template name in 'extends' tag: %r." % parent_name
+                raise TemplateSyntaxError, error_msg
+        self.parent_name_expr = parent_name_expr
 
     def __repr__(self):
         if self.parent_name_expr:
             return "<ExtendsNode: extends %s>" % self.parent_name_expr.token
-        return '<ExtendsNode: extends "%s">' % self.parent_name
+        return '<ExtendsNode: extends "%s">' % self.parent.name
 
     def get_parent(self, context):
-        if self.parent_name_expr:
-            self.parent_name = self.parent_name_expr.resolve(context)
-        parent = self.parent_name
-        if not parent:
-            error_msg = "Invalid template name in 'extends' tag: %r." % parent
-            if self.parent_name_expr:
-                error_msg += " Got this from the '%s' variable." % self.parent_name_expr.token
-            raise TemplateSyntaxError, error_msg
+        if hasattr(self, 'parent'):
+            return self.parent
+        parent = self.parent_name_expr.resolve(context)
         if hasattr(parent, 'render'):
             return parent # parent is a Template object
         try:
-            source, origin = find_template_source(parent, self.template_dirs)
+            return get_template(parent)
         except TemplateDoesNotExist:
-            raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
-        else:
-            return get_template_from_string(source, origin, parent)
+            error_msg = "Invalid template name in 'extends' tag: %r." % parent
+            error_msg += " Got this from the '%s' variable." % self.parent_name_expr.token
+            raise TemplateSyntaxError, error_msg
 
     def render(self, context):
-        compiled_parent = self.get_parent(context)
-        parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
-        for block_node in self.nodelist.get_nodes_by_type(BlockNode):
-            # Check for a BlockNode with this node's name, and replace it if found.
-            try:
-                parent_block = parent_blocks[block_node.name]
-            except KeyError:
-                # This BlockNode wasn't found in the parent template, but the
-                # parent block might be defined in the parent's *parent*, so we
-                # add this BlockNode to the parent's ExtendsNode nodelist, so
-                # it'll be checked when the parent node's render() is called.
-
-                # Find out if the parent template has a parent itself
-                for node in compiled_parent.nodelist:
-                    if not isinstance(node, TextNode):
-                        # If the first non-text node is an extends, handle it.
-                        if isinstance(node, ExtendsNode):
-                            node.nodelist.append(block_node)
-                        # Extends must be the first non-text node, so once you find
-                        # the first non-text node you can stop looking. 
-                        break
-            else:
-                # Keep any existing parents and add a new one. Used by BlockNode.
-                parent_block.parent = block_node.parent
-                parent_block.add_parent(parent_block.nodelist)
-                parent_block.nodelist = block_node.nodelist
-        return compiled_parent.render(context)
+        block_context = context.get(BlockContext.KEY, None)
+        if block_context is None:
+            block_context = BlockContext(context)
+            context[BlockContext.KEY] = block_context
+        block_context.add_blocks(self.block_nodes)
+
+        parent = self.get_parent(context)
+
+        # if parent is the base template, add all its blocks here
+        # we only need to check the first two nodes of the parent to find ExtendsNode
+        #if not parent.nodelist.get_nodes_by_type(ExtendsNode):
+        if not filter(lambda n : isinstance(n, ExtendsNode), parent.nodelist[:2]):
+            if not hasattr(parent, '_block_nodes'):
+                parent._block_nodes = dict([(n.name, n) for n in parent.nodelist.get_nodes_by_type(BlockNode)])
+            block_context.add_blocks(parent._block_nodes)
+
+        return parent.render(context)
 
 class ConstantIncludeNode(Node):
     def __init__(self, template_path):
