refactor: compat with comp as view by reusing comp instance

This commit is contained in:
Juro Oravec 2024-06-11 21:41:56 +02:00
parent 7c52acf411
commit 36cd6e3f06
2 changed files with 53 additions and 1 deletions

View file

@ -228,6 +228,20 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
self.component_id = component_id or gen_id()
self._context: Optional[Context] = None
# When user first instantiates the component class before calling
# `render` or `render_to_response`, then we want to allow the render
# function to make use of the instantiated object.
#
# So while `MyComp.render()` creates a new instance of MyComp internally,
# if we do `MyComp(registered_name="abc").render()`, then we use the
# already-instantiated object.
#
# To achieve that, we want to re-assign the class methods as instance methods.
# For that we have to "unwrap" the class methods via __func__.
# See https://stackoverflow.com/a/76706399/9788634
self.render_to_response = types.MethodType(self.__class__.render_to_response.__func__, self) # type: ignore
self.render = types.MethodType(self.__class__.render.__func__, self) # type: ignore
def __init_subclass__(cls, **kwargs: Any) -> None:
cls._class_hash = hash(inspect.getfile(cls) + cls.__name__)
@ -440,7 +454,13 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
)
```
"""
# This method may be called as class method or as instance method.
# If called as class method, create a new instance.
if isinstance(cls, Component):
comp: Component = cls
else:
comp = cls()
return comp._render(context, args, kwargs, slots, escape_slots_content)
def _render(

View file

@ -477,3 +477,35 @@ class ComponentRenderTest(BaseTestCase):
""",
)
@parametrize_context_behavior(["django", "isolated"])
def test_render_can_access_instance(self):
class TestComponent(component.Component):
template = "Variable: <strong>{{ id }}</strong>"
def get_context_data(self, **attrs):
return {
"id": self.component_id,
}
rendered = TestComponent(component_id="123").render()
self.assertHTMLEqual(
rendered,
"Variable: <strong>123</strong>",
)
@parametrize_context_behavior(["django", "isolated"])
def test_render_to_response_can_access_instance(self):
class TestComponent(component.Component):
template = "Variable: <strong>{{ id }}</strong>"
def get_context_data(self, **attrs):
return {
"id": self.component_id,
}
rendered_resp = TestComponent(component_id="123").render_to_response()
self.assertHTMLEqual(
rendered_resp.content.decode('utf-8'),
"Variable: <strong>123</strong>",
)