mirror of
https://github.com/django-components/django-components.git
synced 2025-08-09 16:57:59 +00:00
fix: populate RequestContext with context processors (#643)
This commit is contained in:
parent
96a4717631
commit
2d0f270df4
2 changed files with 113 additions and 17 deletions
|
@ -1,6 +1,7 @@
|
|||
import inspect
|
||||
import types
|
||||
from collections import deque
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
from typing import (
|
||||
Any,
|
||||
|
@ -8,6 +9,7 @@ from typing import (
|
|||
ClassVar,
|
||||
Deque,
|
||||
Dict,
|
||||
Generator,
|
||||
Generic,
|
||||
List,
|
||||
Literal,
|
||||
|
@ -520,22 +522,7 @@ class Component(Generic[ArgsType, KwargsType, DataType, SlotsType], metaclass=Co
|
|||
context_data = self.get_context_data(*args, **kwargs)
|
||||
self._validate_outputs(context_data)
|
||||
|
||||
with context.update(context_data):
|
||||
template = self.get_template(context)
|
||||
_monkeypatch_template(template)
|
||||
|
||||
if context.template is None:
|
||||
# Associate the newly-created Context with a Template, otherwise we get
|
||||
# an error when we try to use `{% include %}` tag inside the template?
|
||||
# See https://github.com/EmilStenstrom/django-components/issues/580
|
||||
context.template = template
|
||||
context.template_name = template.name
|
||||
|
||||
# Set `Template._dc_is_component_nested` based on whether we're currently INSIDE
|
||||
# the `{% extends %}` tag.
|
||||
# Part of fix for https://github.com/EmilStenstrom/django-components/issues/508
|
||||
template._dc_is_component_nested = bool(context.render_context.get(BLOCK_CONTEXT_KEY))
|
||||
|
||||
with _prepare_template(self, context, context_data) as template:
|
||||
# Support passing slots explicitly to `render` method
|
||||
if has_slots:
|
||||
fill_content = self._fills_from_slots_data(
|
||||
|
@ -843,3 +830,35 @@ def _monkeypatch_template(template: Template) -> None:
|
|||
|
||||
# See https://stackoverflow.com/a/42154067/9788634
|
||||
template.render = types.MethodType(_template_render, template)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _maybe_bind_template(context: Context, template: Template) -> Generator[None, Any, None]:
|
||||
if context.template is None:
|
||||
with context.bind_template(template):
|
||||
yield
|
||||
else:
|
||||
yield
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _prepare_template(
|
||||
component: Component,
|
||||
context: Context,
|
||||
context_data: Any,
|
||||
) -> Generator[Template, Any, None]:
|
||||
with context.update(context_data):
|
||||
# Associate the newly-created Context with a Template, otherwise we get
|
||||
# an error when we try to use `{% include %}` tag inside the template?
|
||||
# See https://github.com/EmilStenstrom/django-components/issues/580
|
||||
# And https://github.com/EmilStenstrom/django-components/issues/634
|
||||
template = component.get_template(context)
|
||||
_monkeypatch_template(template)
|
||||
|
||||
# Set `Template._dc_is_component_nested` based on whether we're currently INSIDE
|
||||
# the `{% extends %}` tag.
|
||||
# Part of fix for https://github.com/EmilStenstrom/django-components/issues/508
|
||||
template._dc_is_component_nested = bool(context.render_context.get(BLOCK_CONTEXT_KEY))
|
||||
|
||||
with _maybe_bind_template(context, template):
|
||||
yield template
|
||||
|
|
|
@ -3,6 +3,7 @@ Tests focusing on the Component class.
|
|||
For tests focusing on the `component` tag, see `test_templatetags_component.py`
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from typing import Any, Dict, Tuple, Union, no_type_check
|
||||
|
||||
|
@ -14,13 +15,16 @@ else:
|
|||
|
||||
from unittest import skipIf
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.template import Context, RequestContext, Template, TemplateSyntaxError
|
||||
from django.template.base import TextNode
|
||||
from django.test import Client
|
||||
from django.urls import path
|
||||
from django.utils.safestring import SafeString
|
||||
|
||||
from django_components import Component, SlotFunc, registry, types
|
||||
from django_components import Component, ComponentView, SlotFunc, register, registry, types
|
||||
from django_components.slots import SlotRef
|
||||
|
||||
from .django_test_setup import setup_test_config
|
||||
|
@ -29,6 +33,21 @@ from .testutils import BaseTestCase, parametrize_context_behavior
|
|||
setup_test_config({"autodiscover": False})
|
||||
|
||||
|
||||
# Client for testing endpoints via requests
|
||||
class CustomClient(Client):
|
||||
def __init__(self, urlpatterns=None, *args, **kwargs):
|
||||
import types
|
||||
|
||||
if urlpatterns:
|
||||
urls_module = types.ModuleType("urls")
|
||||
urls_module.urlpatterns = urlpatterns # type: ignore
|
||||
settings.ROOT_URLCONF = urls_module
|
||||
else:
|
||||
settings.ROOT_URLCONF = __name__
|
||||
settings.SECRET_KEY = "secret" # noqa
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
# Component typings
|
||||
CompArgs = Tuple[int, str]
|
||||
|
||||
|
@ -772,6 +791,7 @@ class ComponentRenderTest(BaseTestCase):
|
|||
)
|
||||
|
||||
# See https://github.com/EmilStenstrom/django-components/issues/580
|
||||
# And https://github.com/EmilStenstrom/django-components/issues/634
|
||||
# And https://github.com/EmilStenstrom/django-components/commit/fee26ec1d8b46b5ee065ca1ce6143889b0f96764
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_with_include_and_request_context(self):
|
||||
|
@ -793,6 +813,63 @@ class ComponentRenderTest(BaseTestCase):
|
|||
""",
|
||||
)
|
||||
|
||||
# See https://github.com/EmilStenstrom/django-components/issues/580
|
||||
# And https://github.com/EmilStenstrom/django-components/issues/634
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_request_context_is_populated_from_context_processors(self):
|
||||
@register("thing")
|
||||
class Thing(Component):
|
||||
template: types.django_html = """
|
||||
<kbd>Rendered {{ how }}</kbd>
|
||||
<div>
|
||||
CSRF token: {{ csrf_token|default:"<em>No CSRF token</em>" }}
|
||||
</div>
|
||||
"""
|
||||
|
||||
def get_context_data(self, *args, how: str, **kwargs):
|
||||
return {"how": how}
|
||||
|
||||
class View(ComponentView):
|
||||
def get(self, request):
|
||||
how = "via GET request"
|
||||
|
||||
return self.component.render_to_response(
|
||||
context=RequestContext(self.request),
|
||||
kwargs=self.component.get_context_data(how=how),
|
||||
)
|
||||
|
||||
client = CustomClient(urlpatterns=[path("test_thing/", Thing.as_view())])
|
||||
response = client.get("/test_thing/")
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Full response:
|
||||
# """
|
||||
# <kbd>
|
||||
# Rendered via GET request
|
||||
# </kbd>
|
||||
# <div>
|
||||
# CSRF token:
|
||||
# <div>
|
||||
# test_csrf_token
|
||||
# </div>
|
||||
# </div>
|
||||
# """
|
||||
self.assertInHTML(
|
||||
"""
|
||||
<kbd>
|
||||
Rendered via GET request
|
||||
</kbd>
|
||||
""",
|
||||
response.content.decode(),
|
||||
)
|
||||
|
||||
token_re = re.compile(rb"CSRF token:\s+(?P<token>[0-9a-zA-Z]{64})")
|
||||
token = token_re.findall(response.content)[0]
|
||||
|
||||
self.assertTrue(token)
|
||||
self.assertEqual(len(token), 64)
|
||||
|
||||
@parametrize_context_behavior(["django", "isolated"])
|
||||
def test_render_with_extends(self):
|
||||
class SimpleComponent(Component):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue