Login

extends_default

Author:
daniellindsley
Posted:
November 5, 2008
Language:
Python
Version:
1.0
Tags:
template templatetag inheritance default
Score:
2 (after 2 ratings)

Works exactly like the standard "extends" tag but enables one to fallback on a default template. This tag is LIMITED, as it falls back to the next template with the same name that DOES NOT contain "extends_default" (does NOT simulate full inheritance, just a single level).

A partial solution to problems like http://jeffcroft.com/blog/2008/aug/05/default-templates-django/.

MIT licensed.

 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
82
83
84
85
86
87
88
89
90
91
92
93
from django.template import TemplateSyntaxError, TemplateDoesNotExist
from django.template import Library, Node, TextNode
from django.template.loader_tags import ExtendsNode
from django.template.loader import get_template_from_string, make_origin
from django.template.loaders.filesystem import get_template_sources
from django.conf import settings

register = Library()

def find_default_template_source(name, dirs=None):
    try:
        source, display_name = load_default_template_source(name, dirs)
        return (source, make_origin(display_name, load_default_template_source, name, dirs))
    except TemplateDoesNotExist:
        raise TemplateDoesNotExist, name

class SawExtendsSelf(IOError): pass

def load_default_template_source(template_name, template_dirs=None):
    tried = []
    for filepath in get_template_sources(template_name, template_dirs):
        try:
            template = open(filepath)
            file_source_line_1 = template.readline()
            if 'load extends_default' in file_source_line_1:
                file_source_line_2 = template.readline()
                if 'extends_default' in file_source_line_2:
                    raise SawExtendsSelf
            template.close()
            return (open(filepath).read().decode(settings.FILE_CHARSET), filepath)
        except SawExtendsSelf:
            tried.append("%s (Saw extends_default and continuing...)" % filepath)
        except IOError:
            tried.append(filepath)
    if tried:
        error_msg = "Tried %s" % tried
    else:
        error_msg = "Your TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory."
    raise TemplateDoesNotExist, error_msg

class ExtendsDefaultNode(ExtendsNode):
    must_be_first = False
    
    def __repr__(self):
        if self.parent_name_expr:
            return "<ExtendsDefaultNode: extends %s>" % self.parent_name_expr.token
        return '<ExtendsDefaultNode: 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_default' 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_default_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 do_extends_default(parser, token):
    """
    Enables extension of a default template with the same name.
    
    Usage:
      {% load extends_default %}
      {% extends_default "same_directory/hierarchy/template_name.html" %}
    
    Works exactly like the standard "extends" tag but enables one to fallback 
    on a default template. This tag is LIMITED, as it falls back to the next 
    template with the same name that DOES NOT contain "extends_default" (does 
    NOT simulate full inheritance, just a single level).
    """
    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(ExtendsDefaultNode) or nodelist.get_nodes_by_type(ExtendsNode):
        raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
    return ExtendsDefaultNode(nodelist, parent_name, parent_name_expr)

register.tag('extends_default', do_extends_default)

More like this

  1. "If in" templatetag by bitprophet 8 years, 3 months ago
  2. Repeat blocks with new context / simple Jinja-like macro system by miracle2k 8 years ago
  3. Seconds-to-Duration Template Tag by danward 6 years, 4 months ago
  4. Inherit the standard url tag to include domain name by atodorov-otb 1 year, 7 months ago
  5. ifcontains tag by dotbg 6 years, 5 months ago

Comments

Please login first before commenting.