Skip to content

Instantly share code, notes, and snippets.

@nkryptic
Last active December 14, 2015 23:39
Show Gist options
  • Save nkryptic/5167139 to your computer and use it in GitHub Desktop.
Save nkryptic/5167139 to your computer and use it in GitHub Desktop.
A conditional block templatetag which outputs it's nodelist only when a sub-template declares a child block with the same name. The child block's output will be available in a context variable, "blockoutput", when inside the "ifblock".

The expected output when rendering index.html is:

<!doctype html>
<head>
  <title>example</title>
</head>
<body>
  <h2>before</h2>
  <h2><span>start</span> <span>the middle</span> <span>end</span></h2>
  <h2>after</h2>
</body>
</html>

The expected output when rendering index2.html is:

<!doctype html>
<head>
  <title>example</title>
</head>
<body>
  <h2>before</h2>
  <h2>after</h2>
</body>
</html>

The expected output when rendering index3.html is:

<!doctype html>
<head>
  <title>example</title>
</head>
<body>
  <h2>before</h2>
  <h2><span>start</span> <span>right in</span> <span>the middle</span> <span>end</span></h2>
  <h2>after</h2>
</body>
</html>
<!doctype html>
<head>
<title>example</title>
</head>
<body>
<h2>before</h2>
{% load ifblock from myapp_tags %}
{% ifblock middle %}
<h2><span>start</span> {{ blockoutput }} <span>end</span></h2>
{% endifblock %}
<h2>after</h2>
</body>
</html>
{% extends "index-base.html" %}
{% block middle %}<span>the middle</span>{% endblock %}
{% extends "index-base.html" %}
{% extends "index.html" %}
{% block middle %}<span>right in</span> {{ block.super }}{% endblock %}
from django.template import Library, TemplateSyntaxError
from django.template.loader_tags import BlockNode, BLOCK_CONTEXT_KEY
register = Library()
class IfBlockNode(BlockNode):
def __repr__(self):
return "<IfBlock Node: %s. Contents: %r>" % (self.name, self.nodelist)
def render(self, context):
block_context = context.render_context.get(BLOCK_CONTEXT_KEY)
context.push()
if block_context is None:
result = ''
else:
push = block = block_context.pop(self.name)
if block is None or block is self:
result = ''
else:
context.push()
# Create new block so we can store context without
# thread-safety issues.
block = IfBlockNode(block.name, block.nodelist)
block.context = context
context['block'] = block
result = block.nodelist.render(context)
context.pop()
context['blockoutput'] = result
result = self.nodelist.render(context)
if push is not None:
block_context.push(self.name, push)
context.pop()
return result
def super(self):
render_context = self.context.render_context
if BLOCK_CONTEXT_KEY in render_context:
block = render_context[BLOCK_CONTEXT_KEY].get_block(self.name)
if block is not None and not isinstance(block, IfBlockNode):
return super(IfBlockNode, self).render(self.context)
return ''
@register.tag(name="ifblock")
def do_ifblock(parser, token):
bits = token.contents.split()
if len(bits) != 2:
raise TemplateSyntaxError("'%s' tag takes only one argument" % bits[0])
block_name = bits[1]
# Keep track of the names of BlockNodes found in this template, so we can
# check for duplication.
try:
if block_name in parser.__loaded_blocks:
msg = "'%s' tag with name '%s' appears more than once"
raise TemplateSyntaxError(msg % (bits[0], block_name))
parser.__loaded_blocks.append(block_name)
except AttributeError: # parser.__loaded_blocks isn't a list yet
parser.__loaded_blocks = [block_name]
nodelist = parser.parse(('endifblock', 'endifblock %s' % block_name))
parser.delete_first_token()
return IfBlockNode(block_name, nodelist)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment