Skip to content

Commit

Permalink
chore
Browse files Browse the repository at this point in the history
  • Loading branch information
michael-yin committed Sep 17, 2024
1 parent 1659d65 commit d3ecd2b
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 96 deletions.
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ lint.ignore = [
"INP001",
"I001",
"FA100",
"FA102",
"SIM118",
"UP031",
"PT011",
]
lint.select = ["ALL"]
exclude = ["migrations"]
Expand Down
2 changes: 1 addition & 1 deletion src/django_viewcomponent/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def get_template(self) -> Template:

raise ImproperlyConfigured(
f"Either 'template_name' or 'template' must be set for Component {type(self).__name__}."
f"Note: this attribute is not required if you are overriding the class's `get_template*()` methods."
f"Note: this attribute is not required if you are overriding the class's `get_template*()` methods.",
)

def prepare_context(
Expand Down
4 changes: 2 additions & 2 deletions src/django_viewcomponent/component_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ class NotRegistered(Exception):
pass


class ComponentRegistry(object):
class ComponentRegistry:
def __init__(self):
self._registry = {} # component name -> component_class mapping

def register(self, name=None, component=None):
existing_component = self._registry.get(name)
if existing_component and existing_component.class_hash != component.class_hash:
raise AlreadyRegistered(
'The component "%s" has already been registered' % name
'The component "%s" has already been registered' % name,
)
self._registry[name] = component

Expand Down
59 changes: 36 additions & 23 deletions src/django_viewcomponent/fields.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
from typing import Optional

from django_viewcomponent.component_registry import registry as component_registry


class FieldValue:
def __init__(
self,
content: str,
dict_data: dict,
component: Optional[str] = None,
component: None,
parent_component=None,
**kwargs,
):
self._content = content or ""
self._dict_data = dict_data
self._content = self._dict_data.pop("content", "")
self._component = component
self._parent_component = parent_component

Expand All @@ -26,18 +24,41 @@ def __str__(self):
def render(self):
from django_viewcomponent.component import Component

component_cls = None
if isinstance(self._component, str):
component_cls = component_registry.get(self._component)
elif issubclass(self._component, Component):
component_cls = self._component
return self._render_for_component_cls(
component_registry.get(self._component),
)
elif not isinstance(self._component, type) and callable(self._component):
# self._component is function
callable_component = self._component
result = callable_component(**self._dict_data)

if isinstance(result, str):
return result
elif isinstance(result, Component):
# render component instance
return self._render_for_component_instance(result)
else:
raise ValueError(
f"Callable slot component must return str or Component instance. Got {result}",
)
elif isinstance(self._component, type) and issubclass(
self._component,
Component,
):
# self._component is Component class
return self._render_for_component_cls(self._component)
else:
raise ValueError(f"Invalid component type {self._component}")
raise ValueError(f"Invalid component variable {self._component}")

def _render_for_component_cls(self, component_cls):
component = component_cls(
**self._dict_data,
)
component.component_name = self._component

return self._render_for_component_instance(component)

def _render_for_component_instance(self, component):
component.component_context = self._parent_component.component_context

with component.component_context.push():
Expand Down Expand Up @@ -86,13 +107,9 @@ def handle_call(self, content, **kwargs):

class RendersOneField(BaseSlotField):
def handle_call(self, content, **kwargs):
value_dict = {
"content": content,
"parent_component": self.parent_component,
**kwargs,
}
value_instance = FieldValue(
dict_data=value_dict,
content=content,
dict_data={**kwargs},
component=self._component,
parent_component=self.parent_component,
)
Expand All @@ -103,13 +120,9 @@ def handle_call(self, content, **kwargs):

class RendersManyField(BaseSlotField):
def handle_call(self, content, **kwargs):
value_dict = {
"content": content,
"parent_component": self.parent_component,
**kwargs,
}
value_instance = FieldValue(
dict_data=value_dict,
content=content,
dict_data={**kwargs},
component=self._component,
parent_component=self.parent_component,
)
Expand Down
3 changes: 2 additions & 1 deletion src/django_viewcomponent/preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def __init_subclass__(cls, **kwargs):
cls.preview_name = new_name
cls.preview_view_component_path = os.path.abspath(inspect.getfile(cls))
cls.url = urljoin(
reverse("django_viewcomponent:preview-index"), cls.preview_name + "/"
reverse("django_viewcomponent:preview-index"),
cls.preview_name + "/",
)

@classmethod
Expand Down
18 changes: 11 additions & 7 deletions src/django_viewcomponent/templatetags/viewcomponent_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,22 @@ def render(self, context):

if "content" in resolved_kwargs:
raise ValueError(
"The 'content' kwarg is reserved and cannot be passed in component call tag"
"The 'content' kwarg is reserved and cannot be passed in component call tag",
)

resolved_kwargs["content"] = content

component_token, field_token = self.args[0].token.split(".")
component_instance = FilterExpression(component_token, self.parser).resolve(
context
context,
)
if not component_instance:
raise ValueError(f"Component {component_token} not found in context")

field = getattr(component_instance, field_token, None)
if not field:
raise ValueError(
f"Field {field_token} not found in component {component_token}"
f"Field {field_token} not found in component {component_token}",
)

if isinstance(field, BaseSlotField):
Expand All @@ -115,7 +115,9 @@ def __repr__(self):
return "<ComponentNode: %s. Contents: %r>" % (
self.name_fexp,
getattr(
self, "nodelist", None
self,
"nodelist",
None,
), # 'nodelist' attribute only assigned later.
)

Expand Down Expand Up @@ -185,7 +187,9 @@ def do_component(parser, token):
bits = bits[:-2]

component_name, context_args, context_kwargs = parse_component_with_arguments(
parser, bits, "component"
parser,
bits,
"component",
)
nodelist: NodeList = parser.parse(parse_until=["endcomponent"])
parser.delete_first_token()
Expand Down Expand Up @@ -217,7 +221,7 @@ def parse_component_with_arguments(parser, bits, tag_name):

if tag_name != tag_args[0].token:
raise RuntimeError(
f"Internal error: Expected tag_name to be {tag_name}, but it was {tag_args[0].token}"
f"Internal error: Expected tag_name to be {tag_name}, but it was {tag_args[0].token}",
)
if len(tag_args) > 1:
# At least one position arg, so take the first as the component name
Expand All @@ -226,7 +230,7 @@ def parse_component_with_arguments(parser, bits, tag_name):
context_kwargs = tag_kwargs
else:
raise TemplateSyntaxError(
f"Call the '{tag_name}' tag with a component name as the first parameter"
f"Call the '{tag_name}' tag with a component name as the first parameter",
)

return component_name, context_args, context_kwargs
Expand Down
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ def pytest_configure():
"django.template.loaders.app_directories.Loader",
"django_viewcomponent.loaders.ComponentLoader",
],
)
),
],
"builtins": [
"django_viewcomponent.templatetags.viewcomponent_tags",
],
},
}
},
],
INSTALLED_APPS=[
"django.contrib.admin",
Expand Down
2 changes: 1 addition & 1 deletion tests/previews/simple_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def with_template_render(self, title="default title", **kwargs):
{% load viewcomponent_tags %}
{% component "example" title=title %}
{% endcomponent %}
"""
""",
)

# pass the title from the URL querystring to the context
Expand Down
4 changes: 2 additions & 2 deletions tests/test_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
class TestLayoutComponents:
def test_html(self):
html = HTML("{% if saved %}Data saved{% endif %}").render_from_parent_context(
{"saved": True}
{"saved": True},
)
assert "Data saved" in html

# step_field and step0 not defined
html = HTML(
'<input type="hidden" name="{{ step_field }}" value="{{ step0 }}" />'
'<input type="hidden" name="{{ step_field }}" value="{{ step0 }}" />',
).render_from_parent_context()
assert_select(html, "input")

Expand Down
4 changes: 2 additions & 2 deletions tests/test_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def test_previews(self, client):
reverse(
"django_viewcomponent:previews",
kwargs={"preview_name": "simple_example_component"},
)
),
)

assert response.status_code == 200
Expand All @@ -49,7 +49,7 @@ def test_preview(self, client):
"preview_name": "simple_example_component",
"example_name": "with_title",
},
)
),
)

assert response.status_code == 200
Expand Down
Loading

0 comments on commit d3ecd2b

Please sign in to comment.