extend tag with cache

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
from django.template.loader_tags import *

register = Library()

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.compiled_parent = None

    def __repr__(self):
        if self.parent_name_expr:
            return "<ExtendsNode: extends %s>" % self.parent_name_expr.token
        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(parent, 'render'):
            return parent # parent is a Template object
        try:
            source, origin = find_template_source(parent, self.template_dirs)
        except TemplateDoesNotExist:
            raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
        else:
            return get_template_from_string(source, origin, parent)

    def process_parent( self, context ):
        compiled_parent = self.get_parent(context)
        pos = 0
        while isinstance(compiled_parent.nodelist[pos], TextNode):
            pos += 1
        parent_is_child = isinstance(compiled_parent.nodelist[pos], ExtendsNode)
        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):
            try:
                parent_block = parent_blocks[block_node.name]
            except KeyError:
                if parent_is_child:
                    compiled_parent.nodelist[pos].nodelist.append(block_node)
            else:
                parent_block.parent = block_node.parent
                parent_block.add_parent(parent_block.nodelist)
                parent_block.nodelist = block_node.nodelist
        return compiled_parent

    def render(self, context):
        if self.compiled_parent:
            compiled_parent = self.compiled_parent
        else:
            compiled_parent = self.process_parent(context)
            # cache it, if static
            if self.parent_name:
                self.compiled_parent = compiled_parent

        return compiled_parent.render(context)


@register.tag
def extends(parser, token):
    bits = token.contents.split()
    if len(bits) != 2:
        raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
    parent_name, parent_name_expr = None, None
    if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
        parent_name = bits[1][1:-1]
    else:
        parent_name_expr = parser.compile_filter(bits[1])
    nodelist = parser.parse()
    if nodelist.get_nodes_by_type(ExtendsNode):
        raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
    return ExtendsNode(nodelist, parent_name, parent_name_expr)

More like this

  1. Parsed RSS into template var by bram 5 years, 9 months ago
  2. Extended db cache backend with 'filter() / LIKE' support (and now scheduled cache clean!) by sleepycal 4 years, 3 months ago
  3. Caching tag with singnal-based invalidation by rushman 6 years, 1 month ago
  4. Feed Reader Inclusion Tag with Caching by baumer1122 6 years, 8 months ago
  5. invalidation of cache-template-tag cache by bram 4 years, 10 months ago

Comments

forgems (on February 21, 2008):

Render method for extends tag is called exactly once per template ( because only one extends tag can exist in template), so template processing is called exactly once. So your code will always call process_parent. No speed gain here.

#

fredd4 (on December 2, 2008):

Here is speed gain. In original extend function from django, it does not remember parsed parent file, so every request there is parser called. It doesn't work if you use render_to_response, becouse this function does not remember parsed tree.

Using my extends, you have about 10-20% faster template rendering. Realy.

#

(Forgotten your password?)