This file centralizes various ways we use Django's Context class pass data across components, nodes, slots, and contexts.
You can think of the Context as our storage system.
copy_forloop_context
copy_forloop_context(from_context: Context, to_context: Context) -> None
Forward the info about the current loop
Source code in src/django_components/context.py
| def copy_forloop_context(from_context: Context, to_context: Context) -> None:
"""Forward the info about the current loop"""
# Note that the ForNode (which implements for loop behavior) does not
# only add the `forloop` key, but also keys corresponding to the loop elements
# So if the loop syntax is `{% for my_val in my_lists %}`, then ForNode also
# sets a `my_val` key.
# For this reason, instead of copying individual keys, we copy the whole stack layer
# set by ForNode.
if "forloop" in from_context:
forloop_dict_index = find_last_index(from_context.dicts, lambda d: "forloop" in d)
to_context.update(from_context.dicts[forloop_dict_index])
|
get_injected_context_var
Retrieve a 'provided' field. The field MUST have been previously 'provided' by the component's ancestors using the {% provide %}
template tag.
Source code in src/django_components/context.py
| def get_injected_context_var(
component_name: str,
context: Context,
key: str,
default: Optional[Any] = None,
) -> Any:
"""
Retrieve a 'provided' field. The field MUST have been previously 'provided'
by the component's ancestors using the `{% provide %}` template tag.
"""
# NOTE: For simplicity, we keep the provided values directly on the context.
# This plays nicely with Django's Context, which behaves like a stack, so "newer"
# values overshadow the "older" ones.
internal_key = _INJECT_CONTEXT_KEY_PREFIX + key
# Return provided value if found
if internal_key in context:
return context[internal_key]
# If a default was given, return that
if default is not None:
return default
# Otherwise raise error
raise KeyError(
f"Component '{component_name}' tried to inject a variable '{key}' before it was provided."
f" To fix this, make sure that at least one ancestor of component '{component_name}' has"
f" the variable '{key}' in their 'provide' attribute."
)
|
prepare_context
prepare_context(context: Context, component_id: str) -> None
Initialize the internal context state.
Source code in src/django_components/context.py
| def prepare_context(
context: Context,
component_id: str,
) -> None:
"""Initialize the internal context state."""
# Initialize mapping dicts within this rendering run.
# This is shared across the whole render chain, thus we set it only once.
if _FILLED_SLOTS_CONTENT_CONTEXT_KEY not in context:
context[_FILLED_SLOTS_CONTENT_CONTEXT_KEY] = {}
set_component_id(context, component_id)
|
set_component_id
set_component_id(context: Context, component_id: str) -> None
We use the Context object to pass down info on inside of which component we are currently rendering.
Source code in src/django_components/context.py
| def set_component_id(context: Context, component_id: str) -> None:
"""
We use the Context object to pass down info on inside of which component
we are currently rendering.
"""
context[_CURRENT_COMP_CONTEXT_KEY] = component_id
|
set_provided_context_var
'Provide' given data under given key. In other words, this data can be retrieved using self.inject(key)
inside of get_context_data()
method of components that are nested inside the {% provide %}
tag.
Source code in src/django_components/context.py
| def set_provided_context_var(
context: Context,
key: str,
provided_kwargs: Dict[str, Any],
) -> None:
"""
'Provide' given data under given key. In other words, this data can be retrieved
using `self.inject(key)` inside of `get_context_data()` method of components that
are nested inside the `{% provide %}` tag.
"""
# NOTE: We raise TemplateSyntaxError since this func should be called only from
# within template.
if not key:
raise TemplateSyntaxError(
"Provide tag received an empty string. Key must be non-empty and a valid identifier."
)
if not key.isidentifier():
raise TemplateSyntaxError(
"Provide tag received a non-identifier string. Key must be non-empty and a valid identifier."
)
# We turn the kwargs into a NamedTuple so that the object that's "provided"
# is immutable. This ensures that the data returned from `inject` will always
# have all the keys that were passed to the `provide` tag.
tpl_cls = namedtuple("DepInject", provided_kwargs.keys()) # type: ignore[misc]
payload = tpl_cls(**provided_kwargs)
internal_key = _INJECT_CONTEXT_KEY_PREFIX + key
context[internal_key] = payload
|