Django

Code

root/django/branches/newforms-admin/django/template/loader_tags.py

Revision 7707, 7.5 kB (checked in by brosner, 6 months ago)

newforms-admin: Merged from trunk up to [7706].

  • Property svn:eol-style set to native
Line 
1 from django.template import TemplateSyntaxError, TemplateDoesNotExist, Variable
2 from django.template import Library, Node, TextNode
3 from django.template.loader import get_template, get_template_from_string, find_template_source
4 from django.conf import settings
5 from django.utils.safestring import mark_safe
6
7 register = Library()
8
9 class ExtendsError(Exception):
10     pass
11
12 class BlockNode(Node):
13     def __init__(self, name, nodelist, parent=None):
14         self.name, self.nodelist, self.parent = name, nodelist, parent
15
16     def __repr__(self):
17         return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
18
19     def render(self, context):
20         context.push()
21         # Save context in case of block.super().
22         self.context = context
23         context['block'] = self
24         result = self.nodelist.render(context)
25         context.pop()
26         return result
27
28     def super(self):
29         if self.parent:
30             return mark_safe(self.parent.render(self.context))
31         return ''
32
33     def add_parent(self, nodelist):
34         if self.parent:
35             self.parent.add_parent(nodelist)
36         else:
37             self.parent = BlockNode(self.name, nodelist)
38
39 class ExtendsNode(Node):
40     must_be_first = True
41
42     def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
43         self.nodelist = nodelist
44         self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
45         self.template_dirs = template_dirs
46
47     def __repr__(self):
48         if self.parent_name_expr:
49             return "<ExtendsNode: extends %s>" % self.parent_name_expr.token
50         return '<ExtendsNode: extends "%s">' % self.parent_name
51
52     def get_parent(self, context):
53         if self.parent_name_expr:
54             self.parent_name = self.parent_name_expr.resolve(context)
55         parent = self.parent_name
56         if not parent:
57             error_msg = "Invalid template name in 'extends' tag: %r." % parent
58             if self.parent_name_expr:
59                 error_msg += " Got this from the '%s' variable." % self.parent_name_expr.token
60             raise TemplateSyntaxError, error_msg
61         if hasattr(parent, 'render'):
62             return parent # parent is a Template object
63         try:
64             source, origin = find_template_source(parent, self.template_dirs)
65         except TemplateDoesNotExist:
66             raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
67         else:
68             return get_template_from_string(source, origin, parent)
69
70     def render(self, context):
71         compiled_parent = self.get_parent(context)
72         parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
73         for block_node in self.nodelist.get_nodes_by_type(BlockNode):
74             # Check for a BlockNode with this node's name, and replace it if found.
75             try:
76                 parent_block = parent_blocks[block_node.name]
77             except KeyError:
78                 # This BlockNode wasn't found in the parent template, but the
79                 # parent block might be defined in the parent's *parent*, so we
80                 # add this BlockNode to the parent's ExtendsNode nodelist, so
81                 # it'll be checked when the parent node's render() is called.
82
83                 # Find out if the parent template has a parent itself
84                 for node in compiled_parent.nodelist:
85                     if not isinstance(node, TextNode):
86                         # If the first non-text node is an extends, handle it.
87                         if isinstance(node, ExtendsNode):
88                             node.nodelist.append(block_node)
89                         # Extends must be the first non-text node, so once you find
90                         # the first non-text node you can stop looking.
91                         break
92             else:
93                 # Keep any existing parents and add a new one. Used by BlockNode.
94                 parent_block.parent = block_node.parent
95                 parent_block.add_parent(parent_block.nodelist)
96                 parent_block.nodelist = block_node.nodelist
97         return compiled_parent.render(context)
98
99 class ConstantIncludeNode(Node):
100     def __init__(self, template_path):
101         try:
102             t = get_template(template_path)
103             self.template = t
104         except:
105             if settings.TEMPLATE_DEBUG:
106                 raise
107             self.template = None
108
109     def render(self, context):
110         if self.template:
111             return self.template.render(context)
112         else:
113             return ''
114
115 class IncludeNode(Node):
116     def __init__(self, template_name):
117         self.template_name = Variable(template_name)
118
119     def render(self, context):
120         try:
121             template_name = self.template_name.resolve(context)
122             t = get_template(template_name)
123             return t.render(context)
124         except TemplateSyntaxError, e:
125             if settings.TEMPLATE_DEBUG:
126                 raise
127             return ''
128         except:
129             return '' # Fail silently for invalid included templates.
130
131 def do_block(parser, token):
132     """
133     Define a block that can be overridden by child templates.
134     """
135     bits = token.contents.split()
136     if len(bits) != 2:
137         raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
138     block_name = bits[1]
139     # Keep track of the names of BlockNodes found in this template, so we can
140     # check for duplication.
141     try:
142         if block_name in parser.__loaded_blocks:
143             raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
144         parser.__loaded_blocks.append(block_name)
145     except AttributeError: # parser.__loaded_blocks isn't a list yet
146         parser.__loaded_blocks = [block_name]
147     nodelist = parser.parse(('endblock', 'endblock %s' % block_name))
148     parser.delete_first_token()
149     return BlockNode(block_name, nodelist)
150
151 def do_extends(parser, token):
152     """
153     Signal that this template extends a parent template.
154
155     This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
156     uses the literal value "base" as the name of the parent template to extend,
157     or ``{% extends variable %}`` uses the value of ``variable`` as either the
158     name of the parent template to extend (if it evaluates to a string) or as
159     the parent tempate itelf (if it evaluates to a Template object).
160     """
161     bits = token.contents.split()
162     if len(bits) != 2:
163         raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
164     parent_name, parent_name_expr = None, None
165     if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
166         parent_name = bits[1][1:-1]
167     else:
168         parent_name_expr = parser.compile_filter(bits[1])
169     nodelist = parser.parse()
170     if nodelist.get_nodes_by_type(ExtendsNode):
171         raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
172     return ExtendsNode(nodelist, parent_name, parent_name_expr)
173
174 def do_include(parser, token):
175     """
176     Loads a template and renders it with the current context.
177
178     Example::
179
180         {% include "foo/some_include" %}
181     """
182     bits = token.contents.split()
183     if len(bits) != 2:
184         raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
185     path = bits[1]
186     if path[0] in ('"', "'") and path[-1] == path[0]:
187         return ConstantIncludeNode(path[1:-1])
188     return IncludeNode(bits[1])
189
190 register.tag('block', do_block)
191 register.tag('extends', do_extends)
192 register.tag('include', do_include)
Note: See TracBrowser for help on using the browser.