refactor: fix compat with include and extends (#1441)

This commit is contained in:
Juro Oravec 2025-10-06 13:08:49 +02:00 committed by GitHub
parent 0be116c7b8
commit 5f0b7905c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 88 additions and 2 deletions

View file

@ -1,7 +1,19 @@
# Release notes
## v0.142.2
_06 Oct 2025_
#### Fix
- Fix compatibility issue when there was multiple `{% include %}` blocks
inside a component fill, while those included templates contained `{% extends %}` tags.
See [#1389](https://github.com/django-components/django-components/issues/1389)
## v0.142.1
_06 Oct 2025_
#### Fix
- Fix bug introduced in v0.142.0 where django-components broke

View file

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "django_components"
version = "0.142.1"
version = "0.142.2"
requires-python = ">=3.8, <4.0"
description = "A way to create simple reusable template components in Django."
keywords = ["django", "components", "css", "js", "html"]

View file

@ -249,7 +249,14 @@ def monkeypatch_include_render(include_node_cls: Type[Node]) -> None:
# NOTE: This implementation is based on Django v5.1.3)
def _include_render(self: IncludeNode, context: Context, *args: Any, **kwargs: Any) -> str:
with context.update({_STRATEGY_CONTEXT_KEY: "ignore"}):
# NOTE: `_STRATEGY_CONTEXT_KEY` is used so that we defer the rendering of components' JS/CSS
# to until the parent template that used the `{% include %}` tag is rendered.
# NOTE: `{ COMPONENT_IS_NESTED_KEY: False }` is used so that a new RenderContext layer is created,
# so that inside each `{% include %}` the template can use the `{% extends %}` tag.
# Otherwise, the state leaks, and if both `{% include %}` templates use the `{% extends %}` tag,
# the second one raises, because it would be like using two `{% extends %}` tags in the same template.
# See https://github.com/django-components/django-components/issues/1389
with context.update({_STRATEGY_CONTEXT_KEY: "ignore", COMPONENT_IS_NESTED_KEY: False}):
return orig_include_render(self, context, *args, **kwargs)
include_node_cls.render = _include_render

View file

@ -1075,3 +1075,70 @@ class TestExtendsCompat:
<p data-djc-id-ca1bc40 data-djc-id-ca1bc42>This template extends another template.</p>
""",
)
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_double_include_template_with_extend(
self,
components_settings,
):
@register("simple_component")
class SimpleComponent(Component):
template: types.django_html = """
{% slot 'content' / %}
"""
# Confirm that this setup works in Django without components
template1: types.django_html = """
{% extends 'block.html' %}
{% load component_tags %}
{% block body %}
{# history: [<Origin name='/Users/presenter/repos/django-components/tests/templates/included.html'>] #}
{% include 'included.html' with variable="INCLUDED 1" %}
{# history: [<Origin name='/Users/presenter/repos/django-components/tests/templates/included.html'>] #}
{% include 'included.html' with variable="INCLUDED 2" %}
{% endblock %}
"""
rendered1 = Template(template1).render(Context())
expected1 = """
<!DOCTYPE html>
<html lang="en">
<body>
<main role="main">
<div class='container main-container'>
Variable: <strong>INCLUDED 1</strong>
Variable: <strong>INCLUDED 2</strong>
</div>
</main>
</body>
</html>
"""
assertHTMLEqual(rendered1, expected1)
template2: types.django_html = """
{% extends 'block.html' %}
{% load component_tags %}
{% block body %}
{% component "simple_component" %}
{% fill "content" %}
{% include 'included.html' with variable="INCLUDED 1" %}
{% include 'included.html' with variable="INCLUDED 2" %}
{% endfill %}
{% endcomponent %}
{% endblock %}
"""
rendered2 = Template(template2).render(Context({"DJC_DEPS_STRATEGY": "ignore"}))
expected2 = """
<!DOCTYPE html>
<html lang="en">
<body>
<main role="main">
<div class='container main-container'>
Variable: <strong data-djc-id-ca1bc40="">INCLUDED 1</strong>
Variable: <strong data-djc-id-ca1bc40="">INCLUDED 2</strong>
</div>
</main>
</body>
</html>
"""
assertHTMLEqual(rendered2, expected2)