class ExtendsNode(Node): must_be_first = True context_key = 'extends_context' def __init__(self, nodelist, parent_name, template_dirs=None): self.nodelist = nodelist self.parent_name = parent_name self.template_dirs = template_dirs self.blocks = {n.name: n for n in nodelist.get_nodes_by_type(BlockNode)} def __repr__(self): return '<ExtendsNode: extends %s>' % self.parent_name.token def find_template(self, template_name, context): """ This is a wrapper around engine.find_template(). A history is kept in the render_context attribute between successive extends calls and passed as the skip argument. This enables extends to work recursively without extending the same template twice. """ # RemovedInDjango20Warning: If any non-recursive loaders are installed # do a direct template lookup. If the same template name appears twice, # raise an exception to avoid system recursion. for loader in context.template.engine.template_loaders: if not loader.supports_recursion: history = context.render_context.setdefault( self.context_key, [context.template.origin.template_name], ) if template_name in history: raise ExtendsError( "Cannot extend templates recursively when using " "non-recursive template loaders", ) template = context.template.engine.get_template(template_name) history.append(template_name) return template history = context.render_context.setdefault( self.context_key, [context.template.origin], ) template, origin = context.template.engine.find_template( template_name, skip=history, ) history.append(origin) return template def get_parent(self, context): parent = self.parent_name.resolve(context) if not parent: error_msg = "Invalid template name in 'extends' tag: %r." % parent if self.parent_name.filters or\ isinstance(self.parent_name.var, Variable): error_msg += " Got this from the '%s' variable." %\ self.parent_name.token raise TemplateSyntaxError(error_msg) if isinstance(parent, Template): # parent is a django.template.Template return parent if isinstance(getattr(parent, 'template', None), Template): # parent is a django.template.backends.django.Template return parent.template return self.find_template(parent, context) def render(self, context): compiled_parent = self.get_parent(context)#獲取父模板對象 if BLOCK_CONTEXT_KEY not in context.render_context: context.render_context[BLOCK_CONTEXT_KEY] = BlockContext()#在context中塊上下文,默認詞典 block_context = context.render_context[BLOCK_CONTEXT_KEY] # Add the block nodes from this node to the block context block_context.add_blocks(self.blocks)#爲塊上下文添加當前頁的塊。 # If this block's parent doesn't have an extends node it is the root, # and its block nodes also need to be added to the block context. for node in compiled_parent.nodelist: # The ExtendsNode has to be the first non-text node. if not isinstance(node, TextNode): if not isinstance(node, ExtendsNode): blocks = {n.name: n for n in#取得父類中的塊節點 compiled_parent.nodelist.get_nodes_by_type(BlockNode)} block_context.add_blocks(blocks)#使得塊節點插到最前面 break # Call Template._render explicitly so the parser context stays # the same. return compiled_parent._render(context)#看塊節點怎麼樣render??? @register.tag('extends') def do_extends(parser, token):#從html文件中生成extend node """ Signal that this template extends a parent template. This tag may be used in two ways: ``{% extends "base" %}`` (with quotes) uses the literal value "base" as the name of the parent template to extend, or ``{% extends variable %}`` uses the value of ``variable`` as either the name of the parent template to extend (if it evaluates to a string) or as the parent template itself (if it evaluates to a Template object). """ bits = token.split_contents() if len(bits) != 2: raise TemplateSyntaxError("'%s' takes one argument" % bits[0]) parent_name = parser.compile_filter(bits[1])#父模板名字 nodelist = parser.parse()#當前html的全部node列表。 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)
class BlockNode(Node): def __init__(self, name, nodelist, parent=None): self.name, self.nodelist, self.parent = name, nodelist, parent def __repr__(self): return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist) def render(self, context): block_context = context.render_context.get(BLOCK_CONTEXT_KEY)#從上下文取得塊node的信息 with context.push(): if block_context is None: context['block'] = self result = self.nodelist.render(context) else: push = block = block_context.pop(self.name)#彈出塊node,排在後面的先出 if block is None: block = self # Create new block so we can store context without thread-safety issues. block = type(self)(block.name, block.nodelist) block.context = context context['block'] = block result = block.nodelist.render(context) if push is not None: block_context.push(self.name, push) return result