This commit is contained in:
Juro Oravec 2025-05-06 21:48:18 +02:00 committed by GitHub
parent 6253042e9e
commit 2dacac1f43
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 73 additions and 6 deletions

View file

@ -370,6 +370,10 @@
# /components/ext/view/components/c1ab2c3?foo=bar#baz
```
#### Fix
- Fix bug: Context processors data was being generated anew for each component. Now the data correctly created once and reused across components with the same request ([#1165](https://github.com/django-components/django-components/issues/1165)).
## v0.139.1
#### Fix

View file

@ -131,7 +131,7 @@ def _copy_block_context(block_context: BlockContext) -> BlockContext:
# See https://github.com/django/django/blame/2d34ebe49a25d0974392583d5bbd954baf742a32/django/template/context.py#L255
def gen_context_processors_data(context: BaseContext, request: HttpRequest) -> Dict[str, Any]:
if request in context_processors_data:
return context_processors_data[request]
return context_processors_data[request].copy()
# TODO_REMOVE_IN_V2 - In v2, if we still support context processors,
# it should be set on our settings, so we wouldn't have to get the Engine for that.
@ -153,4 +153,6 @@ def gen_context_processors_data(context: BaseContext, request: HttpRequest) -> D
except TypeError as e:
raise TypeError(f"Context processor {processor.__qualname__} didn't return a " "dictionary.") from e
context_processors_data[request] = processors_data
return processors_data

View file

@ -1,5 +1,5 @@
import re
from typing import Dict, Optional
from typing import Dict, Optional, cast
import pytest
from django.http import HttpRequest
@ -7,6 +7,7 @@ from django.template import Context, RequestContext, Template
from pytest_django.asserts import assertHTMLEqual, assertInHTML
from django_components import Component, register, registry, types
from django_components.util.misc import gen_id
from django_components.testing import djc_test
from .testutils import PARAMETRIZE_CONTEXT_BEHAVIOR, setup_test_config
@ -14,6 +15,12 @@ from .testutils import PARAMETRIZE_CONTEXT_BEHAVIOR, setup_test_config
setup_test_config({"autodiscover": False})
# Context processor that generates a unique ID. This is used to test that the context
# processor is generated only once, as each time this is called, it should generate a different ID.
def dummy_context_processor(request):
return {"dummy": gen_id()}
#########################
# COMPONENTS
#########################
@ -630,9 +637,7 @@ class TestContextProcessors:
class TestParentComponent(Component):
template: types.django_html = """
{% load component_tags %}
{% component "test_child" %}
{% slot "content" default / %}
{% endcomponent %}
{% slot "content" default / %}
"""
def get_template_data(self, args, kwargs, slots, context):
@ -689,7 +694,7 @@ class TestContextProcessors:
request = HttpRequest()
request_context = RequestContext(request)
rendered = TestComponent.render(request_context)
rendered = TestComponent.render(context=request_context)
assert "csrfmiddlewaretoken" in rendered
assert list(context_processors_data.keys()) == ["csrf_token"] # type: ignore[union-attr]
@ -871,6 +876,62 @@ class TestContextProcessors:
assert list(context_processors_data.keys()) == ["csrf_token"] # type: ignore[union-attr]
assert inner_request == request
@djc_test(django_settings={
"TEMPLATES": [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": ["tests/templates/", "tests/components/"],
"OPTIONS": {
"builtins": [
"django_components.templatetags.component_tags",
],
"context_processors": [
"tests.test_context.dummy_context_processor",
],
},
}
],
})
def test_data_generated_only_once(self):
context_processors_data: Optional[Dict] = None
context_processors_data_child: Optional[Dict] = None
@register("test_parent")
class TestParentComponent(Component):
template: types.django_html = """
{% load component_tags %}
{% component "test_child" / %}
"""
def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data
context_processors_data = self.context_processors_data
return {}
@register("test_child")
class TestChildComponent(Component):
template: types.django_html = """{% csrf_token %}"""
def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data_child
context_processors_data_child = self.context_processors_data
return {}
request = HttpRequest()
TestParentComponent.render(request=request)
parent_data = cast(dict, context_processors_data)
child_data = cast(dict, context_processors_data_child)
# Check that the context processors data is reused across the components with
# the same request.
assert list(parent_data.keys()) == ["csrf_token", "dummy"]
assert list(child_data.keys()) == ["csrf_token", "dummy"]
assert parent_data["dummy"] == "a1bc3f"
assert child_data["dummy"] == "a1bc3f"
assert parent_data["csrf_token"] == child_data["csrf_token"]
def test_raises_on_accessing_context_processors_data_outside_of_rendering(self):
class TestComponent(Component):
template: types.django_html = """{% csrf_token %}"""