Skip to content

Commit

Permalink
Improve component context handling by using Context instead of a fl…
Browse files Browse the repository at this point in the history
…at dict (#131)
  • Loading branch information
joshuadavidthomas authored Jan 22, 2025
1 parent a0e17c8 commit 7694c26
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 27 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ and this project attempts to adhere to [Semantic Versioning](https://semver.org/

## [Unreleased]

### Changed

- Improved component context handling by using `Context` objects directly when a component uses the outside template context, instead of flattening the context `dict`.
- **Internal**: Renamed `only` argument for `BirdNode` to `isolated_context`. This doesn't affect the public API of passing `only` to the template tag.

## [0.11.1]

### Changed
Expand Down
6 changes: 3 additions & 3 deletions src/django_bird/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
from dataclasses import field
from hashlib import md5
from pathlib import Path
from typing import Any

from cachetools import LRUCache
from django.apps import apps
from django.conf import settings
from django.template.backends.django import Template as DjangoTemplate
from django.template.context import Context
from django.template.engine import Engine
from django.template.exceptions import TemplateDoesNotExist
from django.template.loader import select_template
Expand All @@ -33,8 +33,8 @@ def get_asset(self, asset_filename: str) -> Asset | None:
return asset
return None

def render(self, context: dict[str, Any]):
return self.template.render(context)
def render(self, context: Context):
return self.template.template.render(context)

@property
def id(self):
Expand Down
38 changes: 16 additions & 22 deletions src/django_bird/templatetags/tags/bird.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ def do_bird(parser: Parser, token: Token) -> BirdNode:

name = bits[1]
attrs = []
only = False
isolated_context = False

for bit in bits[2:]:
match bit:
case "only":
only = True
isolated_context = True
case "/":
continue
case _:
param = Param.from_bit(bit)
attrs.append(param)

nodelist = parse_nodelist(bits, parser)
return BirdNode(name, attrs, nodelist, only)
return BirdNode(name, attrs, nodelist, isolated_context)


def parse_nodelist(bits: TagBits, parser: Parser) -> NodeList | None:
Expand All @@ -65,19 +65,23 @@ def __init__(
name: str,
attrs: list[Param],
nodelist: NodeList | None,
only: bool = False,
isolated_context: bool = False,
) -> None:
self.name = name
self.attrs = attrs
self.nodelist = nodelist
self.only = only
self.isolated_context = isolated_context

@override
def render(self, context: Context) -> str:
component_name = self.get_component_name(context)
component = components.get_component(component_name)
component_context = self.get_component_context_data(component, context)
return component.render(component_context)

if self.isolated_context:
return component.render(context.new(component_context))
with context.push(**component_context):
return component.render(context)

def get_component_name(self, context: Context) -> str:
try:
Expand All @@ -89,12 +93,6 @@ def get_component_name(self, context: Context) -> str:
def get_component_context_data(
self, component: Component, context: Context
) -> dict[str, Any]:
context_data: dict[str, Any] = {}

if not self.only:
flattened = context.flatten()
context_data = {str(k): v for k, v in flattened.items()}

if app_settings.ENABLE_BIRD_ID_ATTR:
self.attrs.append(Param("data_bird_id", Value(component.id, True)))

Expand All @@ -104,13 +102,9 @@ def get_component_context_data(
slots = Slots.collect(self.nodelist, context).render()
default_slot = slots.get(DEFAULT_SLOT) or context.get("slot")

context_data.update(
{
"attrs": attrs,
"props": props,
"slot": default_slot,
"slots": slots,
}
)

return context_data
return {
"attrs": attrs,
"props": props,
"slot": default_slot,
"slots": slots,
}
5 changes: 3 additions & 2 deletions tests/test_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import pytest
from django.template.backends.django import Template
from django.template.context import Context
from django.template.exceptions import TemplateDoesNotExist
from django.test import override_settings

Expand All @@ -33,7 +34,7 @@ def test_from_name_basic(self, templates_dir):

assert comp.name == "button"
assert isinstance(comp.template, Template)
assert comp.render({}) == "<button>Click me</button>"
assert comp.render(Context({})) == "<button>Click me</button>"

def test_from_name_with_assets(self, templates_dir):
button = TestComponent(
Expand Down Expand Up @@ -103,7 +104,7 @@ def test_from_name_custom_component_dir(self, templates_dir, override_app_settin

assert comp.name == "button"
assert isinstance(comp.template, Template)
assert comp.render({}) == "<button>Click me</button>"
assert comp.render(Context({})) == "<button>Click me</button>"

def test_id_is_consistent(self, templates_dir):
button = TestComponent(
Expand Down

0 comments on commit 7694c26

Please sign in to comment.