Skip to content

Commit 7694c26

Browse files
Improve component context handling by using Context instead of a flat dict (#131)
1 parent a0e17c8 commit 7694c26

File tree

4 files changed

+27
-27
lines changed

4 files changed

+27
-27
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ and this project attempts to adhere to [Semantic Versioning](https://semver.org/
1818

1919
## [Unreleased]
2020

21+
### Changed
22+
23+
- Improved component context handling by using `Context` objects directly when a component uses the outside template context, instead of flattening the context `dict`.
24+
- **Internal**: Renamed `only` argument for `BirdNode` to `isolated_context`. This doesn't affect the public API of passing `only` to the template tag.
25+
2126
## [0.11.1]
2227

2328
### Changed

src/django_bird/components.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
from dataclasses import field
55
from hashlib import md5
66
from pathlib import Path
7-
from typing import Any
87

98
from cachetools import LRUCache
109
from django.apps import apps
1110
from django.conf import settings
1211
from django.template.backends.django import Template as DjangoTemplate
12+
from django.template.context import Context
1313
from django.template.engine import Engine
1414
from django.template.exceptions import TemplateDoesNotExist
1515
from django.template.loader import select_template
@@ -33,8 +33,8 @@ def get_asset(self, asset_filename: str) -> Asset | None:
3333
return asset
3434
return None
3535

36-
def render(self, context: dict[str, Any]):
37-
return self.template.render(context)
36+
def render(self, context: Context):
37+
return self.template.template.render(context)
3838

3939
@property
4040
def id(self):

src/django_bird/templatetags/tags/bird.py

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,20 @@ def do_bird(parser: Parser, token: Token) -> BirdNode:
3232

3333
name = bits[1]
3434
attrs = []
35-
only = False
35+
isolated_context = False
3636

3737
for bit in bits[2:]:
3838
match bit:
3939
case "only":
40-
only = True
40+
isolated_context = True
4141
case "/":
4242
continue
4343
case _:
4444
param = Param.from_bit(bit)
4545
attrs.append(param)
4646

4747
nodelist = parse_nodelist(bits, parser)
48-
return BirdNode(name, attrs, nodelist, only)
48+
return BirdNode(name, attrs, nodelist, isolated_context)
4949

5050

5151
def parse_nodelist(bits: TagBits, parser: Parser) -> NodeList | None:
@@ -65,19 +65,23 @@ def __init__(
6565
name: str,
6666
attrs: list[Param],
6767
nodelist: NodeList | None,
68-
only: bool = False,
68+
isolated_context: bool = False,
6969
) -> None:
7070
self.name = name
7171
self.attrs = attrs
7272
self.nodelist = nodelist
73-
self.only = only
73+
self.isolated_context = isolated_context
7474

7575
@override
7676
def render(self, context: Context) -> str:
7777
component_name = self.get_component_name(context)
7878
component = components.get_component(component_name)
7979
component_context = self.get_component_context_data(component, context)
80-
return component.render(component_context)
80+
81+
if self.isolated_context:
82+
return component.render(context.new(component_context))
83+
with context.push(**component_context):
84+
return component.render(context)
8185

8286
def get_component_name(self, context: Context) -> str:
8387
try:
@@ -89,12 +93,6 @@ def get_component_name(self, context: Context) -> str:
8993
def get_component_context_data(
9094
self, component: Component, context: Context
9195
) -> dict[str, Any]:
92-
context_data: dict[str, Any] = {}
93-
94-
if not self.only:
95-
flattened = context.flatten()
96-
context_data = {str(k): v for k, v in flattened.items()}
97-
9896
if app_settings.ENABLE_BIRD_ID_ATTR:
9997
self.attrs.append(Param("data_bird_id", Value(component.id, True)))
10098

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

107-
context_data.update(
108-
{
109-
"attrs": attrs,
110-
"props": props,
111-
"slot": default_slot,
112-
"slots": slots,
113-
}
114-
)
115-
116-
return context_data
105+
return {
106+
"attrs": attrs,
107+
"props": props,
108+
"slot": default_slot,
109+
"slots": slots,
110+
}

tests/test_components.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import pytest
1212
from django.template.backends.django import Template
13+
from django.template.context import Context
1314
from django.template.exceptions import TemplateDoesNotExist
1415
from django.test import override_settings
1516

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

3435
assert comp.name == "button"
3536
assert isinstance(comp.template, Template)
36-
assert comp.render({}) == "<button>Click me</button>"
37+
assert comp.render(Context({})) == "<button>Click me</button>"
3738

3839
def test_from_name_with_assets(self, templates_dir):
3940
button = TestComponent(
@@ -103,7 +104,7 @@ def test_from_name_custom_component_dir(self, templates_dir, override_app_settin
103104

104105
assert comp.name == "button"
105106
assert isinstance(comp.template, Template)
106-
assert comp.render({}) == "<button>Click me</button>"
107+
assert comp.render(Context({})) == "<button>Click me</button>"
107108

108109
def test_id_is_consistent(self, templates_dir):
109110
button = TestComponent(

0 commit comments

Comments
 (0)