mirror of
https://github.com/django-components/django-components.git
synced 2025-08-04 14:28:18 +00:00
refactor: Update docs and tests to use get_template_data() (#1161)
* refactor: update docs and tests to use get_template_data() * refactor: fix linting * docs: add note about difference between the two methods
This commit is contained in:
parent
c69980493d
commit
28b61c1609
69 changed files with 795 additions and 725 deletions
|
@ -98,8 +98,8 @@ class MyComponent(Component):
|
|||
ttl = 300 # Cache for 5 minutes
|
||||
cache_name = "my_cache"
|
||||
|
||||
def get_context_data(self, name, **kwargs):
|
||||
return {"name": name}
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {"name": kwargs["name"]}
|
||||
```
|
||||
|
||||
In this example, the component's rendered output is cached for 5 minutes using the `my_cache` backend.
|
||||
|
|
|
@ -12,7 +12,7 @@ NOTE: `{% csrf_token %}` tags need access to the top-level context, and they wil
|
|||
|
||||
If you find yourself using the `only` modifier often, you can set the [context_behavior](#context-behavior) option to `"isolated"`, which automatically applies the `only` modifier. This is useful if you want to make sure that components don't accidentally access the outer context.
|
||||
|
||||
Components can also access the outer context in their context methods like `get_context_data` by accessing the property `self.outer_context`.
|
||||
Components can also access the outer context in their context methods like `get_template_data` by accessing the property `self.outer_context`.
|
||||
|
||||
## Example of Accessing Outer Context
|
||||
|
||||
|
@ -22,14 +22,14 @@ Components can also access the outer context in their context methods like `get_
|
|||
</div>
|
||||
```
|
||||
|
||||
Assuming that the rendering context has variables such as `date`, you can use `self.outer_context` to access them from within `get_context_data`. Here's how you might implement it:
|
||||
Assuming that the rendering context has variables such as `date`, you can use `self.outer_context` to access them from within `get_template_data`. Here's how you might implement it:
|
||||
|
||||
```python
|
||||
class Calender(Component):
|
||||
|
||||
...
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
outer_field = self.outer_context["date"]
|
||||
return {
|
||||
"date": outer_fields,
|
||||
|
@ -56,7 +56,7 @@ This has two modes:
|
|||
[`{% with %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#with) tags.
|
||||
- Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#cycle))
|
||||
that the `{% fill %}` tag is part of.
|
||||
- Data returned from [`Component.get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
|
||||
- Data returned from [`Component.get_template_data()`](../../../reference/api#django_components.Component.get_template_data)
|
||||
of the component that owns the fill tag.
|
||||
|
||||
- `"isolated"`
|
||||
|
@ -69,12 +69,12 @@ This has two modes:
|
|||
|
||||
- Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#cycle))
|
||||
that the `{% fill %}` tag is part of.
|
||||
- [`Component.get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
|
||||
- [`Component.get_template_data()`](../../../reference/api#django_components.Component.get_template_data)
|
||||
of the component which defined the template (AKA the "root" component).
|
||||
|
||||
!!! warning
|
||||
|
||||
Notice that the component whose `get_context_data()` we use inside
|
||||
Notice that the component whose `get_template_data()` we use inside
|
||||
[`{% fill %}`](../../../reference/template_tags#fill)
|
||||
is NOT the same across the two modes!
|
||||
|
||||
|
@ -93,10 +93,10 @@ This has two modes:
|
|||
"""
|
||||
```
|
||||
|
||||
- `"django"` - `my_var` has access to data from `get_context_data()` of both `Inner` and `Outer`.
|
||||
- `"django"` - `my_var` has access to data from `get_template_data()` of both `Inner` and `Outer`.
|
||||
If there are variables defined in both, then `Inner` overshadows `Outer`.
|
||||
|
||||
- `"isolated"` - `my_var` has access to data from `get_context_data()` of ONLY `Outer`.
|
||||
- `"isolated"` - `my_var` has access to data from `get_template_data()` of ONLY `Outer`.
|
||||
|
||||
|
||||
### Example "django"
|
||||
|
@ -115,11 +115,11 @@ class RootComp(Component):
|
|||
{% endwith %}
|
||||
"""
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return { "my_var": 123 }
|
||||
```
|
||||
|
||||
Then if [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
|
||||
Then if [`get_template_data()`](../../../reference/api#django_components.Component.get_template_data)
|
||||
of the component `"my_comp"` returns following data:
|
||||
|
||||
```py
|
||||
|
@ -154,11 +154,11 @@ class RootComp(Component):
|
|||
{% endwith %}
|
||||
"""
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return { "my_var": 123 }
|
||||
```
|
||||
|
||||
Then if [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
|
||||
Then if [`get_template_data()`](../../../reference/api#django_components.Component.get_template_data)
|
||||
of the component `"my_comp"` returns following data:
|
||||
|
||||
```py
|
||||
|
@ -172,7 +172,7 @@ Then the template will be rendered as:
|
|||
# cheese
|
||||
```
|
||||
|
||||
Because variables `"my_var"` and `"cheese"` are searched only inside `RootComponent.get_context_data()`.
|
||||
Because variables `"my_var"` and `"cheese"` are searched only inside `RootComponent.get_template_data()`.
|
||||
But since `"cheese"` is not defined there, it's empty.
|
||||
|
||||
!!! info
|
||||
|
|
|
@ -12,9 +12,9 @@ class Calendar(Component):
|
|||
template_file = "template.html"
|
||||
|
||||
# This component takes one parameter, a date string to show in the template
|
||||
def get_context_data(self, date):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": date,
|
||||
"date": kwargs["date"],
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ Extensions can define methods to hook into lifecycle events, such as:
|
|||
- Un/registering a component
|
||||
- Creating or deleting a registry
|
||||
- Pre-processing data passed to a component on render
|
||||
- Post-processing data returned from [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data)
|
||||
- Post-processing data returned from [`get_template_data()`](../../../reference/api#django_components.Component.get_template_data)
|
||||
and others.
|
||||
|
||||
See the full list in [Extension Hooks Reference](../../../reference/extension_hooks).
|
||||
|
@ -122,7 +122,7 @@ For example, the View extension is available as `self.view`:
|
|||
|
||||
```python
|
||||
class MyTable(Component):
|
||||
def get_context_data(self, request):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
# `self.view` points to the instance of `View` extension.
|
||||
return {
|
||||
"view": self.view,
|
||||
|
@ -133,7 +133,7 @@ And the Storybook extension is available as `self.storybook`:
|
|||
|
||||
```python
|
||||
class MyTable(Component):
|
||||
def get_context_data(self, request):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
# `self.storybook` points to the instance of `Storybook` extension.
|
||||
return {
|
||||
"title": self.storybook.title(),
|
||||
|
|
|
@ -78,7 +78,7 @@ In the example from previous section, we've defined two kwargs: `hello="hi" anot
|
|||
|
||||
```py
|
||||
class ChildComponent(Component):
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
my_data = self.inject("my_data")
|
||||
print(my_data.hello) # hi
|
||||
print(my_data.another) # 123
|
||||
|
@ -94,7 +94,7 @@ To avoid the error, you can pass a second argument to [`inject()`](../../../refe
|
|||
|
||||
```py
|
||||
class ChildComponent(Component):
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
my_data = self.inject("invalid_key", DEFAULT_DATA)
|
||||
assert my_data == DEFAULT_DATA
|
||||
return {}
|
||||
|
@ -119,7 +119,7 @@ class ChildComponent(Component):
|
|||
<div> {{ my_data.another }} </div>
|
||||
"""
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
my_data = self.inject("my_data", "default")
|
||||
return {"my_data": my_data}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
When a component is being rendered, the component inputs are passed to various methods like
|
||||
[`get_context_data()`](../../../reference/api#django_components.Component.get_context_data),
|
||||
[`get_template_data()`](../../../reference/api#django_components.Component.get_template_data),
|
||||
[`get_js_data()`](../../../reference/api#django_components.Component.get_js_data),
|
||||
or [`get_css_data()`](../../../reference/api#django_components.Component.get_css_data).
|
||||
|
||||
|
@ -24,10 +24,10 @@ class MyTable(Component):
|
|||
position = "left"
|
||||
selected_items = Default(lambda: [1, 2, 3])
|
||||
|
||||
def get_context_data(self, position, selected_items):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"position": position,
|
||||
"selected_items": selected_items,
|
||||
"position": kwargs["position"],
|
||||
"selected_items": kwargs["selected_items"],
|
||||
}
|
||||
|
||||
...
|
||||
|
@ -138,10 +138,10 @@ class MyTable(Component):
|
|||
position = "left"
|
||||
selected_items = Default(lambda: [1, 2, 3])
|
||||
|
||||
def get_context_data(self, position, selected_items):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"position": position,
|
||||
"selected_items": selected_items,
|
||||
"position": kwargs["position"],
|
||||
"selected_items": kwargs["selected_items"],
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -47,10 +47,10 @@ You can use this for example to allow users of your component to add extra attri
|
|||
```djc_py
|
||||
@register("my_comp")
|
||||
class MyComp(Component):
|
||||
# Capture extra kwargs in `attrs`
|
||||
def get_context_data(self, **attrs):
|
||||
# Pass all kwargs as `attrs`
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"attrs": attrs,
|
||||
"attrs": kwargs,
|
||||
"classes": "text-red",
|
||||
"my_id": 123,
|
||||
}
|
||||
|
@ -607,10 +607,11 @@ class MyComp(Component):
|
|||
</div>
|
||||
"""
|
||||
|
||||
def get_context_data(self, date: Date, attrs: dict):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
date = kwargs.pop("date")
|
||||
return {
|
||||
"date": date,
|
||||
"attrs": attrs,
|
||||
"attrs": kwargs,
|
||||
"class_from_var": "extra-class"
|
||||
}
|
||||
|
||||
|
@ -625,7 +626,7 @@ class Parent(Component):
|
|||
/ %}
|
||||
"""
|
||||
|
||||
def get_context_data(self, date: Date):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": datetime.now(),
|
||||
"json_data": json.dumps({"value": 456})
|
||||
|
@ -669,7 +670,7 @@ So all kwargs that start with `attrs:` will be collected into an `attrs` dict.
|
|||
attrs:@click="(e) => onClick(e, 'from_parent')"
|
||||
```
|
||||
|
||||
And `get_context_data` of `MyComp` will receive `attrs` input with following keys:
|
||||
And `get_template_data` of `MyComp` will receive a kwarg named `attrs` with following keys:
|
||||
|
||||
```py
|
||||
attrs = {
|
||||
|
|
|
@ -100,6 +100,44 @@ class ProfileCard(Component):
|
|||
}
|
||||
```
|
||||
|
||||
There is a slight difference between [`get_context_data()`](../../../reference/api/#django_components.Component.get_context_data) and [`get_template_data()`](../../../reference/api/#django_components.Component.get_template_data)
|
||||
when rendering a component with the [`{% component %}`](../../../reference/template_tags/#component) tag.
|
||||
|
||||
For example if you have component that accepts kwarg `date`:
|
||||
|
||||
```py
|
||||
class MyComponent(Component):
|
||||
def get_context_data(self, date, *args, **kwargs):
|
||||
return {
|
||||
"date": date,
|
||||
}
|
||||
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": kwargs["date"],
|
||||
}
|
||||
```
|
||||
|
||||
The difference is that:
|
||||
|
||||
- With [`get_context_data()`](../../../reference/api/#django_components.Component.get_context_data), you can pass `date` either as arg or kwarg:
|
||||
|
||||
```django
|
||||
✅
|
||||
{% component "my_component" date=some_date %}
|
||||
{% component "my_component" some_date %}
|
||||
```
|
||||
|
||||
- But with [`get_template_data()`](../../../reference/api/#django_components.Component.get_template_data), `date` MUST be passed as kwarg:
|
||||
|
||||
```django
|
||||
✅
|
||||
{% component "my_component" date=some_date %}
|
||||
|
||||
❌
|
||||
{% component "my_component" some_date %}
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
||||
[`get_template_data()`](../../../reference/api/#django_components.Component.get_template_data)
|
||||
|
|
|
@ -48,7 +48,7 @@ When the component has access to the `request` object, the request object will b
|
|||
|
||||
```python
|
||||
class MyComponent(Component):
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
'user_id': self.request.GET['user_id'],
|
||||
}
|
||||
|
@ -77,11 +77,11 @@ class MyComponent(Component):
|
|||
MyComponent.render(request=request)
|
||||
```
|
||||
|
||||
You can also access the context processors data from within [`get_context_data()`](../../../reference/api#django_components.Component.get_context_data) and other methods under [`Component.context_processors_data`](../../../reference/api#django_components.Component.context_processors_data).
|
||||
You can also access the context processors data from within [`get_template_data()`](../../../reference/api#django_components.Component.get_template_data) and other methods under [`Component.context_processors_data`](../../../reference/api#django_components.Component.context_processors_data).
|
||||
|
||||
```python
|
||||
class MyComponent(Component):
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
csrf_token = self.context_processors_data['csrf_token']
|
||||
return {
|
||||
'csrf_token': csrf_token,
|
||||
|
|
|
@ -20,7 +20,7 @@ Example:
|
|||
|
||||
```python
|
||||
class Table(Component):
|
||||
def get_context_data(self, *args, **attrs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
# Access component's ID
|
||||
assert self.id == "c1A2b3c"
|
||||
|
||||
|
@ -77,7 +77,7 @@ If you need to expand this limit, please open an issue on GitHub.
|
|||
|
||||
```python
|
||||
class Table(Component):
|
||||
def get_context_data(self, *args, **attrs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
# Access component's ID
|
||||
assert self.id == "c1A2b3c"
|
||||
|
||||
|
@ -102,7 +102,7 @@ to access the positional and keyword arguments passed to [`Component.render()`](
|
|||
|
||||
```python
|
||||
class Table(Component):
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
# Access component's inputs, slots and context
|
||||
assert self.input.args == [123, "str"]
|
||||
assert self.input.kwargs == {"variable": "test", "another": 1}
|
||||
|
@ -140,7 +140,7 @@ Read more about the request object and context processors in the [HTTP Request](
|
|||
from django.http import HttpRequest
|
||||
|
||||
class Table(Component):
|
||||
def get_context_data(self, *args, **attrs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
# Access the request object and Django's context processors
|
||||
assert self.request.GET == {"query": "something"}
|
||||
assert self.context_processors_data['user'].username == "admin"
|
||||
|
@ -166,7 +166,7 @@ Read more about [Provide / Inject](../advanced/provide_inject.md).
|
|||
|
||||
```python
|
||||
class Table(Component):
|
||||
def get_context_data(self, *args, **attrs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
# Access provided data
|
||||
data = self.inject("some_data")
|
||||
assert data.some_data == "some_data"
|
||||
|
|
|
@ -30,9 +30,9 @@ from django_components import Component, register, types
|
|||
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
def get_context_data(self, date):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": date,
|
||||
"date": kwargs["date"],
|
||||
}
|
||||
|
||||
template: types.django_html = """
|
||||
|
@ -114,9 +114,9 @@ from django_components import Component, register, types
|
|||
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
def get_context_data(self, date):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": date,
|
||||
"date": kwargs["date"],
|
||||
}
|
||||
|
||||
template: types.django_html = """
|
||||
|
@ -155,9 +155,9 @@ from django_components import Component, register
|
|||
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
def get_context_data(self, date):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": date,
|
||||
"date": kwargs["date"],
|
||||
}
|
||||
|
||||
# language=HTML
|
||||
|
|
|
@ -165,8 +165,8 @@ like so:
|
|||
|
||||
```py
|
||||
class MyTable(Component):
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
default_slot = self.input.slots["default"]
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
default_slot = slots["default"]
|
||||
return {
|
||||
"default_slot": default_slot,
|
||||
}
|
||||
|
@ -475,8 +475,8 @@ class MyComp(Component):
|
|||
</div>
|
||||
"""
|
||||
|
||||
def get_context_data(self, input):
|
||||
processed_input = do_something(input)
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
processed_input = do_something(kwargs["input"])
|
||||
return {"input": processed_input}
|
||||
```
|
||||
|
||||
|
@ -504,8 +504,8 @@ class MyComp(Component):
|
|||
</div>
|
||||
"""
|
||||
|
||||
def get_context_data(self, input):
|
||||
processed_input = do_something(input)
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
processed_input = do_something(kwargs["input"])
|
||||
return {
|
||||
"input": processed_input,
|
||||
}
|
||||
|
@ -646,9 +646,9 @@ You can dynamically pass all slots to a child component. This is similar to
|
|||
|
||||
```djc_py
|
||||
class MyTable(Component):
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"slots": self.input.slots,
|
||||
"slots": slots,
|
||||
}
|
||||
|
||||
template = """
|
||||
|
|
|
@ -100,7 +100,7 @@ class BaseForm(Component):
|
|||
</form>
|
||||
"""
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"form_content": self.get_form_content(),
|
||||
"submit_text": "Submit"
|
||||
|
@ -112,8 +112,8 @@ class BaseForm(Component):
|
|||
class ContactForm(BaseForm):
|
||||
# Extend parent's "context"
|
||||
# but override "submit_text"
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
context = super().get_template_data(args, kwargs, slots, context)
|
||||
context["submit_text"] = "Send Message"
|
||||
return context
|
||||
|
||||
|
|
|
@ -30,14 +30,12 @@ so are still valid:
|
|||
</body>
|
||||
```
|
||||
|
||||
These can then be accessed inside `get_context_data` so:
|
||||
These can then be accessed inside `get_template_data` so:
|
||||
|
||||
```py
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
# Since # . @ - are not valid identifiers, we have to
|
||||
# use `**kwargs` so the method can accept these args.
|
||||
def get_context_data(self, **kwargs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": kwargs["my-date"],
|
||||
"id": kwargs["#some_id"],
|
||||
|
@ -92,17 +90,17 @@ _New in version 0.93_
|
|||
When passing data around, sometimes you may need to do light transformations, like negating booleans or filtering lists.
|
||||
|
||||
Normally, what you would have to do is to define ALL the variables
|
||||
inside `get_context_data()`. But this can get messy if your components contain a lot of logic.
|
||||
inside `get_template_data()`. But this can get messy if your components contain a lot of logic.
|
||||
|
||||
```py
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
def get_context_data(self, id: str, editable: bool):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"editable": editable,
|
||||
"readonly": not editable,
|
||||
"input_id": f"input-{id}",
|
||||
"icon_id": f"icon-{id}",
|
||||
"editable": kwargs["editable"],
|
||||
"readonly": not kwargs["editable"],
|
||||
"input_id": f"input-{kwargs['id']}",
|
||||
"icon_id": f"icon-{kwargs['id']}",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
@ -200,10 +198,10 @@ class MyComp(Component):
|
|||
{% component "other" attrs=attrs / %}
|
||||
"""
|
||||
|
||||
def get_context_data(self, some_id: str):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
attrs = {
|
||||
"class": "pa-4 flex",
|
||||
"data-some-id": some_id,
|
||||
"data-some-id": kwargs["some_id"],
|
||||
"@click.stop": "onClickHandler",
|
||||
}
|
||||
return {"attrs": attrs}
|
||||
|
@ -231,8 +229,8 @@ class MyComp(Component):
|
|||
/ %}
|
||||
"""
|
||||
|
||||
def get_context_data(self, some_id: str):
|
||||
return {"some_id": some_id}
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {"some_id": kwargs["some_id"]}
|
||||
```
|
||||
|
||||
Sweet! Now all the relevant HTML is inside the template, and we can move it to a separate file with confidence:
|
||||
|
|
|
@ -184,7 +184,7 @@ class Calendar(Component):
|
|||
js_file = "calendar.js" # <--- new
|
||||
css_file = "calendar.css" # <--- new
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": "1970-01-01",
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ class Calendar(Component):
|
|||
"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", # Tailwind
|
||||
]
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": "1970-01-01",
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ the to `2024-12-14`, which is Saturday, our template from previous step would re
|
|||
|
||||
The first instance rendered `2024-12-16`, while the rest rendered `2024-12-14`!
|
||||
|
||||
Why? Remember that in the [`get_context_data()`](../../reference/api#django_components.Component.get_context_data)
|
||||
Why? Remember that in the [`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
|
||||
method of our Calendar component, we pre-process the date. If the date falls on Saturday or Sunday, we shift it to next Monday:
|
||||
|
||||
```python title="[project root]/components/calendar/calendar.py"
|
||||
|
@ -174,11 +174,11 @@ def to_workweek_date(d: date):
|
|||
class Calendar(Component):
|
||||
template_file = "calendar.html"
|
||||
...
|
||||
def get_context_data(self, date: date, extra_class: str | None = None):
|
||||
workweek_date = to_workweek_date(date)
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
workweek_date = to_workweek_date(kwargs["date"])
|
||||
return {
|
||||
"date": workweek_date,
|
||||
"extra_class": extra_class,
|
||||
"extra_class": kwargs.get("extra_class", "text-blue"),
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -284,7 +284,7 @@ each time:
|
|||
Generally, slots are more flexible - you can access the slot data, even the original slot content.
|
||||
Thus, slots behave more like functions that render content based on their context.
|
||||
|
||||
On the other hand, variables are static - the variable you pass to a component is what will be used.
|
||||
On the other hand, variables are simpler - the variable you pass to a component is what will be used.
|
||||
|
||||
Moreover, slots are treated as part of the template - for example the CSS scoping (work in progress)
|
||||
is applied to the slot content too.
|
||||
|
|
|
@ -30,7 +30,7 @@ class Calendar(Component):
|
|||
js_file = "calendar.js"
|
||||
css_file = "calendar.css"
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": "1970-01-01",
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ What we want is to be able to use the Calendar component within the template lik
|
|||
### 1. Understading component inputs
|
||||
|
||||
In section [Create your first component](../your_first_component), we defined
|
||||
the [`get_context_data()`](../../reference/api#django_components.Component.get_context_data) method
|
||||
the [`get_template_data()`](../../reference/api#django_components.Component.get_template_data) method
|
||||
that defines what variables will be available within the template:
|
||||
|
||||
```python title="[project root]/components/calendar/calendar.py"
|
||||
|
@ -20,13 +20,13 @@ from django_components import Component, register
|
|||
class Calendar(Component):
|
||||
template_file = "calendar.html"
|
||||
...
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": "1970-01-01",
|
||||
}
|
||||
```
|
||||
|
||||
What we didn't say is that [`get_context_data()`](../../reference/api#django_components.Component.get_context_data)
|
||||
What we didn't say is that [`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
|
||||
actually receives the args and kwargs that were passed to a component.
|
||||
|
||||
So if we call a component with a `date` and `extra_class` keywords:
|
||||
|
@ -38,7 +38,10 @@ So if we call a component with a `date` and `extra_class` keywords:
|
|||
This is the same as calling:
|
||||
|
||||
```py
|
||||
Calendar.get_context_data(date="2024-12-13", extra_class="text-red")
|
||||
Calendar.get_template_data(
|
||||
args=[],
|
||||
kwargs={"date": "2024-12-13", "extra_class": "text-red"},
|
||||
)
|
||||
```
|
||||
|
||||
And same applies to positional arguments, or mixing args and kwargs, where:
|
||||
|
@ -50,13 +53,16 @@ And same applies to positional arguments, or mixing args and kwargs, where:
|
|||
is same as
|
||||
|
||||
```py
|
||||
Calendar.get_context_data("2024-12-13", extra_class="text-red")
|
||||
Calendar.get_template_data(
|
||||
args=["2024-12-13"],
|
||||
kwargs={"extra_class": "text-red"},
|
||||
)
|
||||
```
|
||||
|
||||
### 2. Define inputs for `get_context_data`
|
||||
### 2. Define inputs
|
||||
|
||||
Let's put this to test. We want to pass `date` and `extra_class` kwargs to the component.
|
||||
And so, we can write the [`get_context_data()`](../../reference/api#django_components.Component.get_context_data)
|
||||
And so, we can write the [`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
|
||||
method such that it expects those parameters:
|
||||
|
||||
```python title="[project root]/components/calendar/calendar.py"
|
||||
|
@ -68,46 +74,40 @@ from django_components import Component, register
|
|||
class Calendar(Component):
|
||||
template_file = "calendar.html"
|
||||
...
|
||||
def get_context_data(self, date: date, extra_class: str = "text-blue"):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": date,
|
||||
"extra_class": extra_class,
|
||||
"date": kwargs["date"],
|
||||
"extra_class": kwargs.get("extra_class", "text-blue"),
|
||||
}
|
||||
```
|
||||
|
||||
!!! info
|
||||
|
||||
Since [`get_context_data()`](../../reference/api#django_components.Component.get_context_data)
|
||||
is just a regular Python function, type hints annotations work the same way as anywhere else.
|
||||
|
||||
!!! warning
|
||||
|
||||
Since [`get_context_data()`](../../reference/api#django_components.Component.get_context_data)
|
||||
is just a regular Python function, it will raise TypeError if it receives incorrect parameters.
|
||||
|
||||
Since `extra_class` is optional in the function signature, it's optional also in the template.
|
||||
So both following calls are valid:
|
||||
|
||||
```htmldjango
|
||||
{% component "calendar" "2024-12-13" / %}
|
||||
{% component "calendar" "2024-12-13" extra_class="text-red" / %}
|
||||
{% component "calendar" date="2024-12-13" / %}
|
||||
{% component "calendar" date="2024-12-13" extra_class="text-red" / %}
|
||||
```
|
||||
|
||||
However, `date` is required. Thus we MUST provide it. Same with regular Python functions,
|
||||
`date` can be set either as positional or keyword argument. But either way it MUST be set:
|
||||
!!! warning
|
||||
|
||||
```htmldjango
|
||||
✅
|
||||
{% component "calendar" "2024-12-13" / %}
|
||||
{% component "calendar" extra_class="text-red" date="2024-12-13" / %}
|
||||
[`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
|
||||
differentiates between positional and keyword arguments,
|
||||
so you have to make sure to pass the arguments correctly.
|
||||
|
||||
❌
|
||||
{% component "calendar" extra_class="text-red" / %}
|
||||
```
|
||||
Since `date` is expected to be a keyword argument, it MUST be provided as such:
|
||||
|
||||
### 3. Process inputs in `get_context_data`
|
||||
```htmldjango
|
||||
✅ `date` is kwarg
|
||||
{% component "calendar" date="2024-12-13" / %}
|
||||
|
||||
The [`get_context_data()`](../../reference/api#django_components.Component.get_context_data)
|
||||
❌ `date` is arg
|
||||
{% component "calendar" "2024-12-13" / %}
|
||||
```
|
||||
|
||||
### 3. Process inputs
|
||||
|
||||
The [`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
|
||||
method is powerful, because it allows us to decouple
|
||||
component inputs from the template variables. In other words, we can pre-process
|
||||
the component inputs, and massage them into a shape that's most appropriate for
|
||||
|
@ -160,12 +160,14 @@ cities_by_pop = [
|
|||
]
|
||||
```
|
||||
|
||||
Without the `get_context_data()` method, we'd have to either:
|
||||
Without the [`get_template_data()`](../../reference/api#django_components.Component.get_template_data) method,
|
||||
we'd have to either:
|
||||
|
||||
1. Pre-process the data in Python before passing it to the components.
|
||||
2. Define a Django filter or template tag to take the data and process it on the spot.
|
||||
|
||||
Instead, with `get_context_data()`, we can keep this transformation private to this component,
|
||||
Instead, with [`get_template_data()`](../../reference/api#django_components.Component.get_template_data),
|
||||
we can keep this transformation private to this component,
|
||||
and keep the rest of the codebase clean.
|
||||
|
||||
```py
|
||||
|
@ -176,13 +178,14 @@ def group_by_pop(data):
|
|||
class PopulationTable(Component):
|
||||
template_file = "population_table.html"
|
||||
|
||||
def get_context_data(self, data):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"data": group_by_pop(data),
|
||||
"data": group_by_pop(kwargs["data"]),
|
||||
}
|
||||
```
|
||||
|
||||
Similarly we can make use of `get_context_data()` to pre-process the date that was given to the component:
|
||||
Similarly we can make use of [`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
|
||||
to pre-process the date that was given to the component:
|
||||
|
||||
```python title="[project root]/components/calendar/calendar.py"
|
||||
from datetime import date
|
||||
|
@ -197,17 +200,17 @@ def to_workweek_date(d: date):
|
|||
class Calendar(Component):
|
||||
template_file = "calendar.html"
|
||||
...
|
||||
def get_context_data(self, date: date, extra_class: str = "text-blue"):
|
||||
workweek_date = to_workweek_date(date) # <--- new
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
workweek_date = to_workweek_date(kwargs["date"]) # <--- new
|
||||
return {
|
||||
"date": workweek_date, # <--- changed
|
||||
"extra_class": extra_class,
|
||||
"extra_class": kwargs.get("extra_class", "text-blue"),
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Pass inputs to components
|
||||
|
||||
Once we're happy with `Calendar.get_contex_data()`, we can update our templates to use
|
||||
Once we're happy with `Calendar.get_template_data()`, we can update our templates to use
|
||||
the parametrized version of the component:
|
||||
|
||||
```htmldjango
|
||||
|
@ -224,7 +227,7 @@ Next, you will learn [how to use slots give your components even more flexibilit
|
|||
### 5. Add defaults
|
||||
|
||||
In our example, we've set the `extra_class` to default to `"text-blue"` by setting it in the
|
||||
[`get_context_data()`](../../reference/api#django_components.Component.get_context_data)
|
||||
[`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
|
||||
method.
|
||||
|
||||
However, you may want to use the same default value in multiple methods, like
|
||||
|
@ -248,10 +251,10 @@ class Calendar(Component):
|
|||
class Defaults: # <--- new
|
||||
extra_class = "text-blue"
|
||||
|
||||
def get_context_data(self, date: date, extra_class: str): # <--- changed
|
||||
workweek_date = to_workweek_date(date)
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
workweek_date = to_workweek_date(kwargs["date"])
|
||||
return {
|
||||
"date": workweek_date,
|
||||
"extra_class": extra_class,
|
||||
"extra_class": kwargs["extra_class"], # <--- changed
|
||||
}
|
||||
```
|
||||
|
|
|
@ -19,7 +19,7 @@ class Calendar(Component):
|
|||
js_file = "calendar.js"
|
||||
css_file = "calendar.css"
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": "1970-01-01",
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ Inside `calendar.html`, write:
|
|||
```
|
||||
|
||||
In this example we've defined one template variable `date`. You can use any and as many variables as you like. These variables will be
|
||||
defined in the Python file in [`get_context_data()`](../../reference/api#django_components.Component.get_context_data)
|
||||
defined in the Python file in [`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
|
||||
when creating an instance of this component.
|
||||
|
||||
!!! note
|
||||
|
@ -142,7 +142,7 @@ class Calendar(Component):
|
|||
|
||||
In `calendar.html`, we've used the variable `date`. So we need to define it for the template to work.
|
||||
|
||||
This is done using [`Component.get_context_data()`](../../reference/api#django_components.Component.get_context_data).
|
||||
This is done using [`Component.get_template_data()`](../../reference/api#django_components.Component.get_template_data).
|
||||
It's a function that returns a dictionary. The entries in this dictionary
|
||||
will become available within the template as variables, e.g. as `{{ date }}`.
|
||||
|
||||
|
@ -152,7 +152,7 @@ from django_components import Component
|
|||
class Calendar(Component):
|
||||
template_file = "calendar.html"
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": "1970-01-01",
|
||||
}
|
||||
|
|
|
@ -246,6 +246,6 @@ Instead, our solution is closer to [how Vue handles slots](https://vuejs.org/gui
|
|||
|
||||
While we do not wrap the logic in a function, we do PREPARE IN ADVANCE:
|
||||
1. The content that should be rendered for each slot
|
||||
2. The context variables from `get_context_data()`
|
||||
2. The context variables from `get_template_data()`
|
||||
|
||||
Thus, once we reach the `{% slot %}` node, in it's `render()` method, we access the data above, and, depending on the `context_behavior` setting, include the current context or not. For more info, see `SlotNode.render()`.
|
||||
|
|
|
@ -53,8 +53,8 @@ class Calendar(Component):
|
|||
js_file = "calendar.js"
|
||||
css_file = "calendar.css"
|
||||
|
||||
def get_context_data(self, date):
|
||||
return {"date": date}
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {"date": kwargs["date"]}
|
||||
```
|
||||
|
||||
Use the component like this:
|
||||
|
@ -115,9 +115,9 @@ class Calendar(Component):
|
|||
css = ["bootstrap/dist/css/bootstrap.min.css"]
|
||||
|
||||
# Variables available in the template
|
||||
def get_context_data(self, date):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"date": date
|
||||
"date": kwargs["date"]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -222,7 +222,7 @@ class Table(Component):
|
|||
</div>
|
||||
"""
|
||||
|
||||
def get_context_data(self, var1, var2, variable, another, **attrs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
# Access component's ID
|
||||
assert self.id == "djc1A2b3c"
|
||||
|
||||
|
@ -237,7 +237,7 @@ class Table(Component):
|
|||
assert self.context_processors_data['user'].username == "admin"
|
||||
|
||||
return {
|
||||
"variable": variable,
|
||||
"variable": kwargs["variable"],
|
||||
}
|
||||
|
||||
# Access component's HTML / JS / CSS
|
||||
|
@ -337,9 +337,9 @@ class Calendar(Component):
|
|||
},
|
||||
)
|
||||
|
||||
def get_context_data(self, page):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"page": page,
|
||||
"page": kwargs["page"],
|
||||
}
|
||||
|
||||
# Get auto-generated URL for the component
|
||||
|
@ -371,7 +371,7 @@ Read more about [Provide / Inject](https://django-components.github.io/django-co
|
|||
class Header(Component):
|
||||
template = "..."
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
theme = self.inject("theme").variant
|
||||
return {
|
||||
"theme": theme,
|
||||
|
|
|
@ -67,7 +67,7 @@ If you insert this tag multiple times, ALL JS scripts will be duplicately insert
|
|||
|
||||
|
||||
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L2794" target="_blank">See source code</a>
|
||||
<a href="https://github.com/django-components/django-components/tree/master/src/django_components/templatetags/component_tags.py#L2772" target="_blank">See source code</a>
|
||||
|
||||
|
||||
|
||||
|
@ -363,9 +363,9 @@ class Parent(Component):
|
|||
</div>
|
||||
"""
|
||||
|
||||
def get_context_data(self, user: User):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"user": user,
|
||||
"user": kwargs["user"],
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -381,7 +381,7 @@ class Child(Component):
|
|||
</div>
|
||||
"""
|
||||
|
||||
def get_context_data(self):
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
user = self.inject("user_data").user
|
||||
return {
|
||||
"user": user,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue