Skip to content

Commit

Permalink
feat: Add support for multi-level inherited templates
Browse files Browse the repository at this point in the history
This solves the issue where a grandchild template
wants to render a block that does not exist in itself
or the parent template, but exists in the grandparent.
  • Loading branch information
yaakovLowenstein committed Jun 28, 2024
1 parent b2d4b1a commit 47a07e6
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ pip-log.txt

# Editor files
.idea

# Virtual environment
.venv
27 changes: 18 additions & 9 deletions render_block/django.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,33 @@ def django_render_block(template, block_name, context, request=None):

# Bind the template to the context.
with context_instance.bind_template(template):
# Before trying to render the template, we need to traverse the tree of
# parent templates and find all blocks in them.
parent_template = _build_block_context(template, context_instance)
parent_templates = []

# Populates parent_templates with the parent templates of the current
# template, and adds the blocks from those parent templates to the
# context.
_build_block_context(template, context_instance)

try:
return _render_template_block(template, block_name, context_instance)
except BlockNotFound:
# The block wasn't found in the current template.

# If there's no parent template (i.e. no ExtendsNode), re-raise.
if not parent_template:
if not len(parent_templates):
raise

# Check the parent template for this block.
return _render_template_block(parent_template, block_name, context_instance)
for parent_template in parent_templates:
try:
return _render_template_block(
parent_template, block_name, context_instance
)
except BlockNotFound:
continue


def _build_block_context(template, context):
def _build_block_context(template, context, parent_templates):
"""Populate the block context with BlockNodes from parent templates."""

# Ensure there's a BlockContext before rendering. This allows blocks in
Expand All @@ -74,9 +83,9 @@ def _build_block_context(template, context):
}
)

_build_block_context(compiled_parent, context)
return compiled_parent

_build_block_context(compiled_parent, context, parent_templates)
parent_templates.insert(0, compiled_parent)
return parent_templates
# The ExtendsNode has to be the first non-text node.
if not isinstance(node, TextNode):
break
Expand Down
1 change: 1 addition & 0 deletions tests/templates/test7_django.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% block block1 %}block1 from test7{% endblock %}
1 change: 1 addition & 0 deletions tests/templates/test8_django.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends 'test7_django.html' %}
1 change: 1 addition & 0 deletions tests/templates/test9_django.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% extends "test8_django.html" %}
4 changes: 4 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def test_subblock_no_parent(self):
result = render_block_to_string("test_sub.html", "first")
self.assertEqual(result, "\nbar\n")

def test_multi_level_inherited_template_block(self):
result = render_block_to_string("test9_django.html", "block1")
self.assertEqual(result, "block1 from test7")

def test_context(self):
"""Test that a context is properly rendered in a template."""
data = "block2 from test5"
Expand Down

0 comments on commit 47a07e6

Please sign in to comment.