mirror of
https://github.com/django-components/django-components.git
synced 2025-10-15 00:08:59 +00:00
Refactored Component class
# Conflicts: # README.md # pyproject.toml
This commit is contained in:
parent
55f46d6069
commit
5b9188cc9c
8 changed files with 91 additions and 111 deletions
|
@ -189,14 +189,8 @@ from django_components import component
|
|||
|
||||
@component.register("calendar")
|
||||
class Calendar(component.Component):
|
||||
def context(self, date):
|
||||
return {
|
||||
"date": date,
|
||||
}
|
||||
|
||||
# Note that Django will look for templates inside `[your app]/components` dir
|
||||
def template(self, context):
|
||||
return "calendar/calendar.html"
|
||||
template_name = "calendar/calendar.html"
|
||||
|
||||
class Media:
|
||||
css = '[your app]/components/calendar/calendar.css'
|
||||
|
|
|
@ -10,26 +10,26 @@ from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase,
|
|||
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
def template(self, context):
|
||||
return "slotted_template.html"
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
def context(self, variable, variable2="default"):
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def get_context(self, variable, variable2="default"):
|
||||
return {
|
||||
"variable": variable,
|
||||
"variable2": variable2,
|
||||
}
|
||||
|
||||
def template(self, context):
|
||||
return "simple_template.html"
|
||||
|
||||
class Media:
|
||||
css = {"all": ["style.css"]}
|
||||
js = ["script.js"]
|
||||
|
||||
|
||||
class BreadcrumbComponent(component.Component):
|
||||
template_name = "mdn_component_template.html"
|
||||
|
||||
LINKS = [
|
||||
('https://developer.mozilla.org/en-US/docs/Learn',
|
||||
'Learn web development'),
|
||||
|
@ -41,16 +41,13 @@ class BreadcrumbComponent(component.Component):
|
|||
'Document and website structure')
|
||||
]
|
||||
|
||||
def context(self, items):
|
||||
def get_context(self, items):
|
||||
if items > 4:
|
||||
items = 4
|
||||
elif items < 0:
|
||||
items = 0
|
||||
return {'links': self.LINKS[:items - 1]}
|
||||
|
||||
def template(self, context):
|
||||
return "mdn_component_template.html"
|
||||
|
||||
class Media:
|
||||
css = {"all": ["test.css"]}
|
||||
js = ["test.js"]
|
||||
|
|
|
@ -2,6 +2,7 @@ import warnings
|
|||
from functools import lru_cache
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.forms.widgets import MediaDefiningClass
|
||||
from django.template.base import Node, TokenType
|
||||
from django.template.loader import get_template
|
||||
|
@ -41,17 +42,21 @@ class SimplifiedInterfaceMediaDefiningClass(MediaDefiningClass):
|
|||
|
||||
|
||||
class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||
template_name = None
|
||||
|
||||
def __init__(self, component_name):
|
||||
self._component_name = component_name
|
||||
self.instance_template = None
|
||||
self.slots = {}
|
||||
|
||||
def context(self):
|
||||
return {}
|
||||
def get_context(self, *args, **kwargs):
|
||||
return kwargs
|
||||
|
||||
def template(self, context):
|
||||
raise NotImplementedError("Missing template() method on component")
|
||||
def get_template_name(self, context=None):
|
||||
if not self.template_name:
|
||||
raise ImproperlyConfigured(f'Template name is not set for Component {self.__class__.__name__}')
|
||||
|
||||
return self.template_name
|
||||
|
||||
def render_dependencies(self):
|
||||
"""Helper function to access media.render()"""
|
||||
|
@ -112,7 +117,21 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
return component_template
|
||||
|
||||
def render(self, context):
|
||||
template_name = self.template(context)
|
||||
if hasattr(self, 'context'):
|
||||
warnings.warn(
|
||||
f'{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead',
|
||||
DeprecationWarning
|
||||
)
|
||||
|
||||
if hasattr(self, 'template'):
|
||||
warnings.warn(
|
||||
f'{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead',
|
||||
DeprecationWarning
|
||||
)
|
||||
template_name = self.template(context)
|
||||
else:
|
||||
template_name = self.get_template_name(context)
|
||||
|
||||
instance_template = self.get_processed_template(template_name)
|
||||
with context.update({ACTIVE_SLOT_CONTEXT_KEY: self.slots}):
|
||||
return instance_template.render(context)
|
||||
|
|
|
@ -149,7 +149,7 @@ class ComponentNode(Node):
|
|||
# context method to get values to insert into the context
|
||||
resolved_context_args = [safe_resolve(arg, context) for arg in self.context_args]
|
||||
resolved_context_kwargs = {key: safe_resolve(kwarg, context) for key, kwarg in self.context_kwargs.items()}
|
||||
component_context = self.component.context(*resolved_context_args, **resolved_context_kwargs)
|
||||
component_context = self.component.get_context(*resolved_context_args, **resolved_context_kwargs)
|
||||
|
||||
# Create a fresh context if requested
|
||||
if self.isolated_context:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from textwrap import dedent
|
||||
|
||||
from django.template import Context, Template
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
|
||||
|
@ -13,25 +14,24 @@ class ComponentTest(SimpleTestCase):
|
|||
class EmptyComponent(component.Component):
|
||||
pass
|
||||
|
||||
with self.assertRaises(NotImplementedError):
|
||||
EmptyComponent("empty_component").template({})
|
||||
with self.assertRaises(ImproperlyConfigured):
|
||||
EmptyComponent("empty_component").get_template_name()
|
||||
|
||||
def test_simple_component(self):
|
||||
class SimpleComponent(component.Component):
|
||||
def context(self, variable=None):
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def get_context(self, variable=None):
|
||||
return {
|
||||
"variable": variable,
|
||||
}
|
||||
|
||||
def template(self, context):
|
||||
return "simple_template.html"
|
||||
|
||||
class Media:
|
||||
css = "style.css"
|
||||
js = "script.js"
|
||||
|
||||
comp = SimpleComponent("simple_component")
|
||||
context = Context(comp.context(variable="test"))
|
||||
context = Context(comp.get_context(variable="test"))
|
||||
|
||||
self.assertHTMLEqual(comp.render_dependencies(), dedent("""
|
||||
<link href="style.css" type="text/css" media="all" rel="stylesheet">
|
||||
|
@ -59,17 +59,16 @@ class ComponentTest(SimpleTestCase):
|
|||
|
||||
def test_component_with_filtered_template(self):
|
||||
class FilteredComponent(component.Component):
|
||||
def context(self, var1=None, var2=None):
|
||||
template_name = "filtered_template.html"
|
||||
|
||||
def get_context(self, var1=None, var2=None):
|
||||
return {
|
||||
"var1": var1,
|
||||
"var2": var2,
|
||||
}
|
||||
|
||||
def template(self, context):
|
||||
return "filtered_template.html"
|
||||
|
||||
comp = FilteredComponent("filtered_component")
|
||||
context = Context(comp.context(var1="test1", var2="test2"))
|
||||
context = Context(comp.get_context(var1="test1", var2="test2"))
|
||||
|
||||
self.assertHTMLEqual(comp.render(context), dedent("""
|
||||
Var1: <strong>test1</strong>
|
||||
|
@ -78,21 +77,21 @@ class ComponentTest(SimpleTestCase):
|
|||
|
||||
def test_component_with_dynamic_template(self):
|
||||
class SvgComponent(component.Component):
|
||||
def context(self, name, css_class="", title="", **attrs):
|
||||
def get_context(self, name, css_class="", title="", **attrs):
|
||||
return {"name": name, "css_class": css_class, "title": title, **attrs}
|
||||
|
||||
def template(self, context):
|
||||
def get_template_name(self, context):
|
||||
return f"svg_{context['name']}.svg"
|
||||
|
||||
comp = SvgComponent("svg_component")
|
||||
self.assertHTMLEqual(
|
||||
comp.render(Context(comp.context(name="dynamic1"))),
|
||||
comp.render(Context(comp.get_context(name="dynamic1"))),
|
||||
dedent("""\
|
||||
<svg>Dynamic1</svg>
|
||||
""")
|
||||
)
|
||||
self.assertHTMLEqual(
|
||||
comp.render(Context(comp.context(name="dynamic2"))),
|
||||
comp.render(Context(comp.get_context(name="dynamic2"))),
|
||||
dedent("""\
|
||||
<svg>Dynamic2</svg>
|
||||
""")
|
||||
|
@ -171,8 +170,7 @@ class ComponentMediaTests(SimpleTestCase):
|
|||
class ComponentIsolationTests(SimpleTestCase):
|
||||
def setUp(self):
|
||||
class SlottedComponent(component.Component):
|
||||
def template(self, context):
|
||||
return "slotted_template.html"
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
component.registry.register('test', SlottedComponent)
|
||||
|
||||
|
@ -221,13 +219,11 @@ class RecursiveSlotNameTest(SimpleTestCase):
|
|||
def setUp(self):
|
||||
@component.register('outer')
|
||||
class OuterComponent(component.Component):
|
||||
def template(self, context):
|
||||
return "slotted_template.html"
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
@component.register('inner')
|
||||
class InnerComponent(component.Component):
|
||||
def template(self, context):
|
||||
return "slotted_template.html"
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
def test_no_infinite_recursion_when_slot_name_is_reused(self):
|
||||
template = Template(
|
||||
|
|
|
@ -8,11 +8,10 @@ from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
|||
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
def context(self, variable=None):
|
||||
return {"variable": variable} if variable is not None else {}
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def template(self, context):
|
||||
return "simple_template.html"
|
||||
def get_context(self, variable=None):
|
||||
return {"variable": variable} if variable is not None else {}
|
||||
|
||||
@staticmethod
|
||||
def expected_output(variable_value):
|
||||
|
@ -20,27 +19,25 @@ class SimpleComponent(component.Component):
|
|||
|
||||
|
||||
class ParentComponent(component.Component):
|
||||
def context(self):
|
||||
template_name = "parent_template.html"
|
||||
|
||||
def get_context(self):
|
||||
return {
|
||||
"shadowing_variable": 'NOT SHADOWED'
|
||||
}
|
||||
|
||||
def template(self, context):
|
||||
return "parent_template.html"
|
||||
|
||||
|
||||
class ParentComponentWithArgs(component.Component):
|
||||
def context(self, parent_value):
|
||||
return {
|
||||
"inner_parent_value": parent_value
|
||||
}
|
||||
template_name = "parent_with_args_template.html"
|
||||
|
||||
def template(self, context):
|
||||
return "parent_with_args_template.html"
|
||||
def get_context(self, parent_value):
|
||||
return {"inner_parent_value": parent_value}
|
||||
|
||||
|
||||
class VariableDisplay(component.Component):
|
||||
def context(self, shadowing_variable=None, new_variable=None):
|
||||
template_name = "variable_display.html"
|
||||
|
||||
def get_context(self, shadowing_variable=None, new_variable=None):
|
||||
context = {}
|
||||
if shadowing_variable is not None:
|
||||
context['shadowing_variable'] = shadowing_variable
|
||||
|
@ -48,12 +45,11 @@ class VariableDisplay(component.Component):
|
|||
context['unique_variable'] = new_variable
|
||||
return context
|
||||
|
||||
def template(self, context):
|
||||
return "variable_display.html"
|
||||
|
||||
|
||||
class IncrementerComponent(component.Component):
|
||||
def context(self, value=0):
|
||||
template_name = "incrementer.html"
|
||||
|
||||
def get_context(self, value=0):
|
||||
value = int(value)
|
||||
if hasattr(self, 'call_count'):
|
||||
self.call_count += 1
|
||||
|
@ -64,16 +60,13 @@ class IncrementerComponent(component.Component):
|
|||
"calls": self.call_count
|
||||
}
|
||||
|
||||
def template(self, context):
|
||||
return "incrementer.html"
|
||||
|
||||
|
||||
class OuterContextComponent(component.Component):
|
||||
def context(self):
|
||||
return self.outer_context
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def template(self, context):
|
||||
return "simple_template.html"
|
||||
def get_context(self):
|
||||
return self.outer_context
|
||||
|
||||
|
||||
component.registry.register(name='parent_component', component=ParentComponent)
|
||||
|
|
|
@ -9,11 +9,7 @@ from .testutils import create_and_process_template_response, Django30CompatibleS
|
|||
|
||||
|
||||
class SimpleComponentAlternate(component.Component):
|
||||
def context(self, variable):
|
||||
return {}
|
||||
|
||||
def template(self, context):
|
||||
return "simple_template.html"
|
||||
template_name = "simple_template.html"
|
||||
|
||||
class Media:
|
||||
css = "style2.css"
|
||||
|
@ -21,23 +17,21 @@ class SimpleComponentAlternate(component.Component):
|
|||
|
||||
|
||||
class SimpleComponentWithSharedDependency(component.Component):
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def context(self, variable, variable2="default"):
|
||||
return {
|
||||
"variable": variable,
|
||||
"variable2": variable2,
|
||||
}
|
||||
|
||||
def template(self, context):
|
||||
return "simple_template.html"
|
||||
|
||||
class Media:
|
||||
css = ["style.css", "style2.css"]
|
||||
js = ["script.js", "script2.js"]
|
||||
|
||||
|
||||
class MultistyleComponent(component.Component):
|
||||
def template(self, context):
|
||||
return "simple_template.html"
|
||||
template_name = "simple_template.html"
|
||||
|
||||
class Media:
|
||||
css = ["style.css", "style2.css"]
|
||||
|
|
|
@ -8,59 +8,51 @@ from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
|||
|
||||
|
||||
class SimpleComponent(component.Component):
|
||||
def context(self, variable, variable2="default"):
|
||||
template_name = "simple_template.html"
|
||||
|
||||
def get_context(self, variable, variable2="default"):
|
||||
return {
|
||||
"variable": variable,
|
||||
"variable2": variable2,
|
||||
}
|
||||
|
||||
def template(self, context):
|
||||
return "simple_template.html"
|
||||
|
||||
class Media:
|
||||
css = "style.css"
|
||||
js = "script.js"
|
||||
|
||||
|
||||
class IffedComponent(SimpleComponent):
|
||||
def template(self, context):
|
||||
return "iffed_template.html"
|
||||
template_name = "iffed_template.html"
|
||||
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
def template(self, context):
|
||||
return "slotted_template.html"
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
|
||||
class BrokenComponent(component.Component):
|
||||
def template(self, context):
|
||||
return "template_with_illegal_slot.html"
|
||||
template_name = "template_with_illegal_slot.html"
|
||||
|
||||
|
||||
class SlottedComponentWithMissingVariable(component.Component):
|
||||
def template(self, context):
|
||||
return "slotted_template_with_missing_variable.html"
|
||||
template_name = "slotted_template_with_missing_variable.html"
|
||||
|
||||
|
||||
class SlottedComponentNoSlots(component.Component):
|
||||
def template(self, context):
|
||||
return "slotted_template_no_slots.html"
|
||||
template_name = "slotted_template_no_slots.html"
|
||||
|
||||
|
||||
class SlottedComponentWithContext(component.Component):
|
||||
def context(self, variable):
|
||||
return {"variable": variable}
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
def template(self, context):
|
||||
return "slotted_template.html"
|
||||
def get_context(self, variable):
|
||||
return {"variable": variable}
|
||||
|
||||
|
||||
class ComponentWithProvidedAndDefaultParameters(component.Component):
|
||||
def context(self, variable, default_param="default text"):
|
||||
return {"variable": variable, 'default_param': default_param}
|
||||
template_name = "template_with_provided_and_default_parameters.html"
|
||||
|
||||
def template(self, context):
|
||||
return "template_with_provided_and_default_parameters.html"
|
||||
def get_context(self, variable, default_param="default text"):
|
||||
return {"variable": variable, 'default_param': default_param}
|
||||
|
||||
|
||||
class ComponentTemplateTagTest(SimpleTestCase):
|
||||
|
@ -364,11 +356,7 @@ class TemplateInstrumentationTest(SimpleTestCase):
|
|||
|
||||
class NestedSlotTests(SimpleTestCase):
|
||||
class NestedComponent(component.Component):
|
||||
def context(self):
|
||||
return {}
|
||||
|
||||
def template(self, context):
|
||||
return "nested_slot_template.html"
|
||||
template_name = "nested_slot_template.html"
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
@ -427,11 +415,10 @@ class NestedSlotTests(SimpleTestCase):
|
|||
|
||||
class ConditionalSlotTests(SimpleTestCase):
|
||||
class ConditionalComponent(component.Component):
|
||||
def context(self, branch=None):
|
||||
return {'branch': branch}
|
||||
template_name = "conditional_template.html"
|
||||
|
||||
def template(self, context):
|
||||
return "conditional_template.html"
|
||||
def get_context(self, branch=None):
|
||||
return {'branch': branch}
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue