Skip to content

Releases: django-components/django-components

0.143.2

12 Nov 16:07
1d0649e

Choose a tag to compare

What's Changed

Fix

  • Fix KeyError when using {% provide %}, caused by premature cleanup
    by @joeyjurjens in #1490

New Contributors

Full Changelog: 0.143.1...0.143.2

0.143.1

09 Nov 20:26
40fa9c6

Choose a tag to compare

What's Changed

Fix

  • Make django-component's position in Django's INSTALLED_APPS more lenient by not calling Django's URLResolver._populate() if URLResolver hasn't been resolved before (#1483).

Refactor

  • Components now raise error if template data overwrites variables from context_processors (#1482)

Full Changelog: 0.143.0...0.143.1

0.143.0

21 Oct 22:41
1b0caaa

Choose a tag to compare

What's Changed

Added support for Python 3.14!

Feat

  • You can now define component input defaults directly on Component.Kwargs.

    Before, the defaults had to be defined on a separate Component.Defaults class:

    class ProfileCard(Component):
        class Kwargs:
            user_id: int
            show_details: bool
    
        class Defaults:
            show_details = True

    Now, django-components can detect the defaults from Component.Kwargs and apply
    them. So you can merge Component.Kwargs with Component.Defaults:

    class ProfileCard(Component):
        class Kwargs:
            user_id: int
            show_details: bool = True

    NOTE: This applies only when Component.Kwargs is a NamedTuple or dataclass.

  • New helper get_component_defaults():

    Now, the defaults may be defined on either Component.Defaults and Component.Kwargs classes.

    To get a final, merged dictionary of all the component's defaults, use get_component_defaults():

    from django_components import Component, Default, get_component_defaults
    
    class MyTable(Component):
        class Kwargs:
            position: str
            order: int
            items: list[int]
            variable: str = "from_kwargs"
    
        class Defaults:
            position: str = "left"
            items = Default(lambda: [1, 2, 3])
    
    defaults = get_component_defaults(MyTable)
    # {
    #     "position": "left",
    #     "items": [1, 2, 3],
    #     "variable": "from_kwargs",
    # }
  • Simpler syntax for defining component inputs:

    When defining Args, Kwargs, Slots, JsData, CssData, TemplateData, these data classes now don't have to subclass any other class.

    If they are not subclassing (nor @dataclass), these data classes will be automatically converted to NamedTuples:

    Before - the Args, Kwargs, and Slots (etc..) had to be NamedTuples, dataclasses, or Pydantic models:

    from typing import NamedTuple
    from django_components import Component
    
    class Button(Component):
        class Args(NamedTuple):
            size: int
            text: str
    
        class Kwargs(NamedTuple):
            variable: str
            maybe_var: Optional[int] = None
    
        class Slots(NamedTuple):
            my_slot: Optional[SlotInput] = None
    
        def get_template_data(self, args: Args, kwargs: Kwargs, slots: Slots, context: Context):
            ...

    Now these classes are automatically converted to NamedTuples if they don't subclass anything else:

    class Button(Component):
        class Args:  # Same as `Args(NamedTuple)`
            size: int
            text: str
    
        class Kwargs:  # Same as `Kwargs(NamedTuple)`
            variable: str
            maybe_var: Optional[int] = None
    
        class Slots:  # Same as `Slots(NamedTuple)`
            my_slot: Optional[SlotInput] = None
    
        def get_template_data(self, args: Args, kwargs: Kwargs, slots: Slots, context: Context):
            ...

Refactor

  • Extension authors: The ExtensionComponentConfig can be instantiated with None instead of a component instance.

    This allows to call component-level extension methods outside of the normal rendering lifecycle.

Full Changelog: 0.142.3...0.143.0

0.142.3

17 Oct 09:11
aaa0f42

Choose a tag to compare

What's Changed

v0.142.3

Fix

  • Fixed compatibility with older versions of django-template-partials. django-components now works with django-template-partials v23.3 and later. (See #1455)

Refactor

  • Component.View.public = True is now optional.

    Before, to create component endpoints, you had to set both:

    1. HTTP handlers on Component.View
    2. Component.View.public = True.

    Now, you can set only the HTTP handlers, and the component will be automatically exposed
    when any of the HTTP handlers are defined.

    You can still explicitly expose/hide the component with Component.View.public = True/False.

    Before:

    class MyTable(Component):
        class View:
            public = True
    
            def get(self, request):
                return self.render_to_response()
    
    url = get_component_url(MyTable)

    After:

    class MyTable(Component):
        class View:
            def get(self, request):
                return self.render_to_response()
    
    url = get_component_url(MyTable)

Docs

New Contributors

Full Changelog: 0.142.2...0.142.3

0.142.2

06 Oct 11:12
5f0b790

Choose a tag to compare

What's Changed

Fix

  • Fix compatibility issue when there was multiple {% include %} blocks inside a component fill, while those included templates contained {% extends %} tags. See #1389

Full Changelog: 0.142.1...0.142.2

0.142.1

06 Oct 09:07
a08d051

Choose a tag to compare

What's Changed

Fix

  • Fix bug introduced in v0.142.0 where django-components broke
    when the {% component_tags %} library was NOT among the built-ins (#1439)

  • Fix compatibility between Django's inclusion_tag and django-components.
    (#1390)

Full Changelog: 0.142.0...0.142.1

0.142.0

05 Oct 07:17
ba90f4e

Choose a tag to compare

⚠️ This version is broken, please update to v0.142.1 ⚠️

What's Changed

v0.142.0

Feat

  • New built-in component ErrorFallback

    Use ErrorFallback to catch errors and display a fallback content instead.

    This is similar to React's ErrorBoundary
    component.

    Either pass the fallback as a kwarg:

    {% component "error_fallback" fallback="Oops, something went wrong" %}
        {% component "table" / %}
    {% endcomponent %}

    Or use the full fallback slot:

    {% component "error_fallback" %}
        {% fill "content" %}
            {% component "table" / %}
        {% endfill %}
        {% fill "fallback" data="data" %}
            <p>Oops, something went wrong</p>
            {% button href="/report-error" %}
                Report error
            {% endbutton %}
        {% endfill %}
    {% endcomponent %}
  • Wrap the template rendering in Component.on_render() in a lambda function.

    When you wrap the rendering call in a lambda function, and the rendering fails,
    the error will be yielded back in the (None, Exception) tuple.

    Before:

    class MyTable(Component):
        def on_render(self, context, template):
            try:
                intermediate = template.render(context)
                html, error = yield intermediate
            except Exception as e:
                html, error = None, e

    After:

    class MyTable(Component):
        def on_render(self, context, template):
            html, error = yield lambda: template.render(context)
  • Multiple yields in Component.on_render() - You can now yield multiple times within the same on_render method for complex rendering scenarios.

    class MyTable(Component):
        def on_render(self, context, template):
            # First yield
            with context.push({"mode": "header"}):
                header_html, header_error = yield lambda: template.render(context)
            
            # Second yield
            with context.push({"mode": "body"}):
                body_html, body_error = yield lambda: template.render(context)
            
            # Third yield
            footer_html, footer_error = yield "Footer content"
            
            # Process all results
            if header_error or body_error or footer_error:
                return "Error occurred during rendering"
            
            return f"{header_html}\n{body_html}\n{footer_html}"

    Each yield operation is independent and returns its own (html, error) tuple, allowing you to handle each rendering result separately.

Fix

  • Improve formatting when an exception is raised while rendering components. Error messages with newlines should now be properly formatted.

  • Add missing exports for OnComponentRenderedContext, OnSlotRenderedContext, OnTemplateCompiledContext, OnTemplateLoadedContext.

Refactor

  • Changes to how get_component_url() handles query parameters:

    • True values are now converted to boolean flags (e.g. ?enabled instead of ?enabled=True).
    • False and None values are now filtered out.
    url = get_component_url(
        MyComponent,
        query={"abc": 123, "enabled": True, "debug": False, "none_key": None},
    )
    # /components/ext/view/components/c1ab2c3?abc=123&enabled

Docs

  • New people page to celebrate the contributors and authors!

Full Changelog: 0.141.6...0.142.0

0.141.6

29 Sep 14:11
9afc89e

Choose a tag to compare

What's Changed

Fix

  • Fix error that occured when calling Component.inject() inside loops:

    class MyComponent(Component):
        def get_template_data(self, args, kwargs, slots, context):
            data = self.inject("my_provide")
            return {"data": data}
    {% load component_tags %}
    {% provide "my_provide" key="hi" data=data %}
        {% for i in range(10) %}
            {% component "my_component" / %}
        {% endfor %}
    {% endprovide %}
  • Allow to call Component.inject() outside of the rendering:

    comp = None
    
    class MyComponent(Component):
        def get_template_data(self, args, kwargs, slots, context):
            nonlocal comp
            comp = self
    
    template_str = """
        {% load component_tags %}
        {% provide "my_provide" key="hi" data=data %}
            {% component "my_component" / %}
        {% endprovide %}
    """
    template = Template(template_str)
    rendered = template.render(Context({}))
    
    assert comp is not None
    
    injected = comp.inject("my_provide")
    assert injected.key == "hi"
    assert injected.data == "data"

Refactor

  • Removed circular references to the Component instances. Component instances
    are now garbage collected unless you keep a reference to them.

Docs

Full Changelog: 0.141.5...0.141.6

0.141.5

10 Sep 20:37
b770f83

Choose a tag to compare

What's Changed

Fix

  • Tests - Fix bug when using @djc_test decorator and the COMPONENTS
    settings are set with ComponentsSettings by @PavelPancocha.
    See #1369

Docs

  • Add 2-level tabs, add examples and plugins, move community, split release notes by @JuroOravec in #1345

New Contributors

Full Changelog: 0.141.4...0.141.5

0.141.4

15 Aug 08:44
9be4124

Choose a tag to compare

What's Changed

Fix

  • Fix compatibility with Django's {% include %} and {% extends %} tags (#1325)

Full Changelog: 0.141.3...0.141.4