Skip to content

Commit

Permalink
Render specific blocks from templates (useful for AJAX, alternative)
Browse files Browse the repository at this point in the history
Special thanks to the author of snippet 769 who provided most of the code for this snippet.

Major differences:

1.Simpler/better handling of "extends" block tag 2.Searches If/Else blocks 3.Less code 4.Allow list of templates to be passed which is closer to the behavior of render_to_response
  • Loading branch information
zbyte64 authored and clokep committed May 18, 2016
1 parent c6f7bc7 commit caad74e
Showing 1 changed file with 26 additions and 51 deletions.
77 changes: 26 additions & 51 deletions template.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,45 @@
# file template.py

import new
from django.template.loader_tags import BlockNode, ExtendsNode
from django.template import loader, Context, RequestContext, TextNode

def get_template(template):
if isinstance(template, (tuple, list)):
return loader.select_template(template)
return loader.get_template(template)

class BlockNotFound(Exception):
pass

class ExtendsNodeMixin(object):
def compile(self, context):
"""
Compiles this node and returns the compiled parent.
"""
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):
# 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.
if parent_is_child:
compiled_parent.nodelist[pos].nodelist.append(block_node)
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

ExtendsNode.__bases__ += (ExtendsNodeMixin,)

def render(self, context):
self.compiled_parent = self.compile(context)
return self.compiled_parent.render(context)

ExtendsNode.render = new.instancemethod(render, None, ExtendsNode)

def render_template_block(template, block, context):
"""
Renders a single block from a template. This template should have previously been rendered.
"""
if len(template.nodelist) and not isinstance(template.nodelist[0], ExtendsNode):
for blk in template.nodelist:
if isinstance(blk, BlockNode) and blk.name == block:
return blk.render(context)
raise BlockNotFound
for blk in template.nodelist[0].nodelist:
if isinstance(blk, BlockNode) and blk.name == block:
return blk.render(context)
return render_template_block(template.nodelist[0].compiled_parent, block, context)
return render_template_block_nodelist(template.nodelist, block, context)

def render_template_block_nodelist(nodelist, block, context):
for node in nodelist:
if isinstance(node, BlockNode) and node.name == block:
return node.render(context)
for key in ('nodelist', 'nodelist_true', 'nodelist_false'):
if hasattr(node, key):
try:
return render_template_block_nodelist(getattr(node, key), block, context)
except:
pass
for node in nodelist:
if isinstance(node, ExtendsNode):
try:
return render_template_block(node.get_parent(context), block, context)
except BlockNotFound:
pass
raise BlockNotFound

def render_block_to_string(template_name, block, dictionary=None, context_instance=None):
"""
Loads the given template_name and renders the given block with the given dictionary as
context. Returns a string.
"""
dictionary = dictionary or {}
t = loader.get_template(template_name)
t = get_template(template_name)
if context_instance:
context_instance.update(dictionary)
else:
Expand All @@ -86,6 +61,6 @@ def direct_block_to_template(request, template, block, extra_context=None, mimet
else:
dictionary[key] = value
c = RequestContext(request, dictionary)
t = loader.get_template(template)
t = get_template(template)
t.render(c)
return HttpResponse(render_template_block(t, block, c), mimetype=mimetype)

0 comments on commit caad74e

Please sign in to comment.