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:
Juro Oravec 2025-05-03 12:04:10 +02:00 committed by GitHub
parent c69980493d
commit 28b61c1609
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 795 additions and 725 deletions

View file

@ -63,8 +63,8 @@ class Calendar(Component):
js_file = "calendar.js" js_file = "calendar.js"
css_file = "calendar.css" css_file = "calendar.css"
def get_context_data(self, date): def get_template_data(self, args, kwargs, slots, context):
return {"date": date} return {"date": kwargs["date"]}
``` ```
Use the component like this: Use the component like this:
@ -125,9 +125,9 @@ class Calendar(Component):
css = ["bootstrap/dist/css/bootstrap.min.css"] css = ["bootstrap/dist/css/bootstrap.min.css"]
# Variables available in the template # Variables available in the template
def get_context_data(self, date): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": date "date": kwargs["date"]
} }
``` ```
@ -232,7 +232,7 @@ class Table(Component):
</div> </div>
""" """
def get_context_data(self, var1, var2, variable, another, **attrs): def get_template_data(self, args, kwargs, slots, context):
# Access component's ID # Access component's ID
assert self.id == "djc1A2b3c" assert self.id == "djc1A2b3c"
@ -247,7 +247,7 @@ class Table(Component):
assert self.context_processors_data['user'].username == "admin" assert self.context_processors_data['user'].username == "admin"
return { return {
"variable": variable, "variable": kwargs["variable"],
} }
# Access component's HTML / JS / CSS # Access component's HTML / JS / CSS
@ -347,9 +347,9 @@ class Calendar(Component):
}, },
) )
def get_context_data(self, page): def get_template_data(self, args, kwargs, slots, context):
return { return {
"page": page, "page": kwargs["page"],
} }
# Get auto-generated URL for the component # Get auto-generated URL for the component
@ -381,7 +381,7 @@ Read more about [Provide / Inject](https://django-components.github.io/django-co
class Header(Component): class Header(Component):
template = "..." template = "..."
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
theme = self.inject("theme").variant theme = self.inject("theme").variant
return { return {
"theme": theme, "theme": theme,

View file

@ -98,8 +98,8 @@ class MyComponent(Component):
ttl = 300 # Cache for 5 minutes ttl = 300 # Cache for 5 minutes
cache_name = "my_cache" cache_name = "my_cache"
def get_context_data(self, name, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return {"name": name} return {"name": kwargs["name"]}
``` ```
In this example, the component's rendered output is cached for 5 minutes using the `my_cache` backend. In this example, the component's rendered output is cached for 5 minutes using the `my_cache` backend.

View file

@ -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. 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 ## Example of Accessing Outer Context
@ -22,14 +22,14 @@ Components can also access the outer context in their context methods like `get_
</div> </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 ```python
class Calender(Component): class Calender(Component):
... ...
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
outer_field = self.outer_context["date"] outer_field = self.outer_context["date"]
return { return {
"date": outer_fields, "date": outer_fields,
@ -56,7 +56,7 @@ This has two modes:
[`{% with %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#with) tags. [`{% 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)) - Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#cycle))
that the `{% fill %}` tag is part of. 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. of the component that owns the fill tag.
- `"isolated"` - `"isolated"`
@ -69,12 +69,12 @@ This has two modes:
- Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#cycle)) - Any loops ([`{% for ... %}`](https://docs.djangoproject.com/en/5.1/ref/templates/builtins/#cycle))
that the `{% fill %}` tag is part of. 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). of the component which defined the template (AKA the "root" component).
!!! warning !!! 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) [`{% fill %}`](../../../reference/template_tags#fill)
is NOT the same across the two modes! 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`. 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" ### Example "django"
@ -115,11 +115,11 @@ class RootComp(Component):
{% endwith %} {% endwith %}
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { "my_var": 123 } 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: of the component `"my_comp"` returns following data:
```py ```py
@ -154,11 +154,11 @@ class RootComp(Component):
{% endwith %} {% endwith %}
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { "my_var": 123 } 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: of the component `"my_comp"` returns following data:
```py ```py
@ -172,7 +172,7 @@ Then the template will be rendered as:
# cheese # 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. But since `"cheese"` is not defined there, it's empty.
!!! info !!! info

View file

@ -12,9 +12,9 @@ class Calendar(Component):
template_file = "template.html" template_file = "template.html"
# This component takes one parameter, a date string to show in the template # 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 { return {
"date": date, "date": kwargs["date"],
} }
``` ```

View file

@ -42,7 +42,7 @@ Extensions can define methods to hook into lifecycle events, such as:
- Un/registering a component - Un/registering a component
- Creating or deleting a registry - Creating or deleting a registry
- Pre-processing data passed to a component on render - 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. and others.
See the full list in [Extension Hooks Reference](../../../reference/extension_hooks). 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 ```python
class MyTable(Component): 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. # `self.view` points to the instance of `View` extension.
return { return {
"view": self.view, "view": self.view,
@ -133,7 +133,7 @@ And the Storybook extension is available as `self.storybook`:
```python ```python
class MyTable(Component): 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. # `self.storybook` points to the instance of `Storybook` extension.
return { return {
"title": self.storybook.title(), "title": self.storybook.title(),

View file

@ -78,7 +78,7 @@ In the example from previous section, we've defined two kwargs: `hello="hi" anot
```py ```py
class ChildComponent(Component): class ChildComponent(Component):
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
my_data = self.inject("my_data") my_data = self.inject("my_data")
print(my_data.hello) # hi print(my_data.hello) # hi
print(my_data.another) # 123 print(my_data.another) # 123
@ -94,7 +94,7 @@ To avoid the error, you can pass a second argument to [`inject()`](../../../refe
```py ```py
class ChildComponent(Component): 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) my_data = self.inject("invalid_key", DEFAULT_DATA)
assert my_data == DEFAULT_DATA assert my_data == DEFAULT_DATA
return {} return {}
@ -119,7 +119,7 @@ class ChildComponent(Component):
<div> {{ my_data.another }} </div> <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") my_data = self.inject("my_data", "default")
return {"my_data": my_data} return {"my_data": my_data}

View file

@ -1,5 +1,5 @@
When a component is being rendered, the component inputs are passed to various methods like 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), [`get_js_data()`](../../../reference/api#django_components.Component.get_js_data),
or [`get_css_data()`](../../../reference/api#django_components.Component.get_css_data). or [`get_css_data()`](../../../reference/api#django_components.Component.get_css_data).
@ -24,10 +24,10 @@ class MyTable(Component):
position = "left" position = "left"
selected_items = Default(lambda: [1, 2, 3]) 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 { return {
"position": position, "position": kwargs["position"],
"selected_items": selected_items, "selected_items": kwargs["selected_items"],
} }
... ...
@ -138,10 +138,10 @@ class MyTable(Component):
position = "left" position = "left"
selected_items = Default(lambda: [1, 2, 3]) 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 { return {
"position": position, "position": kwargs["position"],
"selected_items": selected_items, "selected_items": kwargs["selected_items"],
} }
``` ```

View file

@ -47,10 +47,10 @@ You can use this for example to allow users of your component to add extra attri
```djc_py ```djc_py
@register("my_comp") @register("my_comp")
class MyComp(Component): class MyComp(Component):
# Capture extra kwargs in `attrs` # Pass all kwargs as `attrs`
def get_context_data(self, **attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"attrs": attrs, "attrs": kwargs,
"classes": "text-red", "classes": "text-red",
"my_id": 123, "my_id": 123,
} }
@ -607,10 +607,11 @@ class MyComp(Component):
</div> </div>
""" """
def get_context_data(self, date: Date, attrs: dict): def get_template_data(self, args, kwargs, slots, context):
date = kwargs.pop("date")
return { return {
"date": date, "date": date,
"attrs": attrs, "attrs": kwargs,
"class_from_var": "extra-class" "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 { return {
"date": datetime.now(), "date": datetime.now(),
"json_data": json.dumps({"value": 456}) "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')" 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 ```py
attrs = { attrs = {

View file

@ -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 !!! warning
[`get_template_data()`](../../../reference/api/#django_components.Component.get_template_data) [`get_template_data()`](../../../reference/api/#django_components.Component.get_template_data)

View file

@ -48,7 +48,7 @@ When the component has access to the `request` object, the request object will b
```python ```python
class MyComponent(Component): class MyComponent(Component):
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
'user_id': self.request.GET['user_id'], 'user_id': self.request.GET['user_id'],
} }
@ -77,11 +77,11 @@ class MyComponent(Component):
MyComponent.render(request=request) 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 ```python
class MyComponent(Component): 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'] csrf_token = self.context_processors_data['csrf_token']
return { return {
'csrf_token': csrf_token, 'csrf_token': csrf_token,

View file

@ -20,7 +20,7 @@ Example:
```python ```python
class Table(Component): class Table(Component):
def get_context_data(self, *args, **attrs): def get_template_data(self, args, kwargs, slots, context):
# Access component's ID # Access component's ID
assert self.id == "c1A2b3c" assert self.id == "c1A2b3c"
@ -77,7 +77,7 @@ If you need to expand this limit, please open an issue on GitHub.
```python ```python
class Table(Component): class Table(Component):
def get_context_data(self, *args, **attrs): def get_template_data(self, args, kwargs, slots, context):
# Access component's ID # Access component's ID
assert self.id == "c1A2b3c" assert self.id == "c1A2b3c"
@ -102,7 +102,7 @@ to access the positional and keyword arguments passed to [`Component.render()`](
```python ```python
class Table(Component): 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 # Access component's inputs, slots and context
assert self.input.args == [123, "str"] assert self.input.args == [123, "str"]
assert self.input.kwargs == {"variable": "test", "another": 1} 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 from django.http import HttpRequest
class Table(Component): 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 # Access the request object and Django's context processors
assert self.request.GET == {"query": "something"} assert self.request.GET == {"query": "something"}
assert self.context_processors_data['user'].username == "admin" assert self.context_processors_data['user'].username == "admin"
@ -166,7 +166,7 @@ Read more about [Provide / Inject](../advanced/provide_inject.md).
```python ```python
class Table(Component): class Table(Component):
def get_context_data(self, *args, **attrs): def get_template_data(self, args, kwargs, slots, context):
# Access provided data # Access provided data
data = self.inject("some_data") data = self.inject("some_data")
assert data.some_data == "some_data" assert data.some_data == "some_data"

View file

@ -30,9 +30,9 @@ from django_components import Component, register, types
@register("calendar") @register("calendar")
class Calendar(Component): class Calendar(Component):
def get_context_data(self, date): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": date, "date": kwargs["date"],
} }
template: types.django_html = """ template: types.django_html = """
@ -114,9 +114,9 @@ from django_components import Component, register, types
@register("calendar") @register("calendar")
class Calendar(Component): class Calendar(Component):
def get_context_data(self, date): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": date, "date": kwargs["date"],
} }
template: types.django_html = """ template: types.django_html = """
@ -155,9 +155,9 @@ from django_components import Component, register
@register("calendar") @register("calendar")
class Calendar(Component): class Calendar(Component):
def get_context_data(self, date): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": date, "date": kwargs["date"],
} }
# language=HTML # language=HTML

View file

@ -165,8 +165,8 @@ like so:
```py ```py
class MyTable(Component): class MyTable(Component):
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
default_slot = self.input.slots["default"] default_slot = slots["default"]
return { return {
"default_slot": default_slot, "default_slot": default_slot,
} }
@ -475,8 +475,8 @@ class MyComp(Component):
</div> </div>
""" """
def get_context_data(self, input): def get_template_data(self, args, kwargs, slots, context):
processed_input = do_something(input) processed_input = do_something(kwargs["input"])
return {"input": processed_input} return {"input": processed_input}
``` ```
@ -504,8 +504,8 @@ class MyComp(Component):
</div> </div>
""" """
def get_context_data(self, input): def get_template_data(self, args, kwargs, slots, context):
processed_input = do_something(input) processed_input = do_something(kwargs["input"])
return { return {
"input": processed_input, "input": processed_input,
} }
@ -646,9 +646,9 @@ You can dynamically pass all slots to a child component. This is similar to
```djc_py ```djc_py
class MyTable(Component): class MyTable(Component):
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"slots": self.input.slots, "slots": slots,
} }
template = """ template = """

View file

@ -100,7 +100,7 @@ class BaseForm(Component):
</form> </form>
""" """
def get_context_data(self, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"form_content": self.get_form_content(), "form_content": self.get_form_content(),
"submit_text": "Submit" "submit_text": "Submit"
@ -112,8 +112,8 @@ class BaseForm(Component):
class ContactForm(BaseForm): class ContactForm(BaseForm):
# Extend parent's "context" # Extend parent's "context"
# but override "submit_text" # but override "submit_text"
def get_context_data(self, **kwargs): def get_template_data(self, args, kwargs, slots, context):
context = super().get_context_data(**kwargs) context = super().get_template_data(args, kwargs, slots, context)
context["submit_text"] = "Send Message" context["submit_text"] = "Send Message"
return context return context

View file

@ -30,14 +30,12 @@ so are still valid:
</body> </body>
``` ```
These can then be accessed inside `get_context_data` so: These can then be accessed inside `get_template_data` so:
```py ```py
@register("calendar") @register("calendar")
class Calendar(Component): class Calendar(Component):
# Since # . @ - are not valid identifiers, we have to def get_template_data(self, args, kwargs, slots, context):
# use `**kwargs` so the method can accept these args.
def get_context_data(self, **kwargs):
return { return {
"date": kwargs["my-date"], "date": kwargs["my-date"],
"id": kwargs["#some_id"], "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. 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 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 ```py
@register("calendar") @register("calendar")
class Calendar(Component): class Calendar(Component):
def get_context_data(self, id: str, editable: bool): def get_template_data(self, args, kwargs, slots, context):
return { return {
"editable": editable, "editable": kwargs["editable"],
"readonly": not editable, "readonly": not kwargs["editable"],
"input_id": f"input-{id}", "input_id": f"input-{kwargs['id']}",
"icon_id": f"icon-{id}", "icon_id": f"icon-{kwargs['id']}",
... ...
} }
``` ```
@ -200,10 +198,10 @@ class MyComp(Component):
{% component "other" attrs=attrs / %} {% component "other" attrs=attrs / %}
""" """
def get_context_data(self, some_id: str): def get_template_data(self, args, kwargs, slots, context):
attrs = { attrs = {
"class": "pa-4 flex", "class": "pa-4 flex",
"data-some-id": some_id, "data-some-id": kwargs["some_id"],
"@click.stop": "onClickHandler", "@click.stop": "onClickHandler",
} }
return {"attrs": attrs} return {"attrs": attrs}
@ -231,8 +229,8 @@ class MyComp(Component):
/ %} / %}
""" """
def get_context_data(self, some_id: str): def get_template_data(self, args, kwargs, slots, context):
return {"some_id": some_id} 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: Sweet! Now all the relevant HTML is inside the template, and we can move it to a separate file with confidence:

View file

@ -184,7 +184,7 @@ class Calendar(Component):
js_file = "calendar.js" # <--- new js_file = "calendar.js" # <--- new
css_file = "calendar.css" # <--- new css_file = "calendar.css" # <--- new
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": "1970-01-01", "date": "1970-01-01",
} }
@ -256,7 +256,7 @@ class Calendar(Component):
"https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css", # Tailwind "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 { return {
"date": "1970-01-01", "date": "1970-01-01",
} }

View file

@ -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`! 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: 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" ```python title="[project root]/components/calendar/calendar.py"
@ -174,11 +174,11 @@ def to_workweek_date(d: date):
class Calendar(Component): class Calendar(Component):
template_file = "calendar.html" template_file = "calendar.html"
... ...
def get_context_data(self, date: date, extra_class: str | None = None): def get_template_data(self, args, kwargs, slots, context):
workweek_date = to_workweek_date(date) workweek_date = to_workweek_date(kwargs["date"])
return { return {
"date": workweek_date, "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. 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. 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) Moreover, slots are treated as part of the template - for example the CSS scoping (work in progress)
is applied to the slot content too. is applied to the slot content too.

View file

@ -30,7 +30,7 @@ class Calendar(Component):
js_file = "calendar.js" js_file = "calendar.js"
css_file = "calendar.css" css_file = "calendar.css"
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": "1970-01-01", "date": "1970-01-01",
} }

View file

@ -10,7 +10,7 @@ What we want is to be able to use the Calendar component within the template lik
### 1. Understading component inputs ### 1. Understading component inputs
In section [Create your first component](../your_first_component), we defined 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: that defines what variables will be available within the template:
```python title="[project root]/components/calendar/calendar.py" ```python title="[project root]/components/calendar/calendar.py"
@ -20,13 +20,13 @@ from django_components import Component, register
class Calendar(Component): class Calendar(Component):
template_file = "calendar.html" template_file = "calendar.html"
... ...
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": "1970-01-01", "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. 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: 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: This is the same as calling:
```py ```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: 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 is same as
```py ```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. 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: method such that it expects those parameters:
```python title="[project root]/components/calendar/calendar.py" ```python title="[project root]/components/calendar/calendar.py"
@ -68,46 +74,40 @@ from django_components import Component, register
class Calendar(Component): class Calendar(Component):
template_file = "calendar.html" 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 { return {
"date": date, "date": kwargs["date"],
"extra_class": extra_class, "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. Since `extra_class` is optional in the function signature, it's optional also in the template.
So both following calls are valid: So both following calls are valid:
```htmldjango ```htmldjango
{% component "calendar" "2024-12-13" / %} {% component "calendar" date="2024-12-13" / %}
{% component "calendar" "2024-12-13" extra_class="text-red" / %} {% component "calendar" date="2024-12-13" extra_class="text-red" / %}
``` ```
However, `date` is required. Thus we MUST provide it. Same with regular Python functions, !!! warning
`date` can be set either as positional or keyword argument. But either way it MUST be set:
```htmldjango [`get_template_data()`](../../reference/api#django_components.Component.get_template_data)
differentiates between positional and keyword arguments,
{% component "calendar" "2024-12-13" / %} so you have to make sure to pass the arguments correctly.
{% component "calendar" extra_class="text-red" date="2024-12-13" / %}
Since `date` is expected to be a keyword argument, it MUST be provided as such:
{% component "calendar" extra_class="text-red" / %}
```
### 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 method is powerful, because it allows us to decouple
component inputs from the template variables. In other words, we can pre-process 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 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. 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. 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. and keep the rest of the codebase clean.
```py ```py
@ -176,13 +178,14 @@ def group_by_pop(data):
class PopulationTable(Component): class PopulationTable(Component):
template_file = "population_table.html" template_file = "population_table.html"
def get_context_data(self, data): def get_template_data(self, args, kwargs, slots, context):
return { 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" ```python title="[project root]/components/calendar/calendar.py"
from datetime import date from datetime import date
@ -197,17 +200,17 @@ def to_workweek_date(d: date):
class Calendar(Component): class Calendar(Component):
template_file = "calendar.html" 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):
workweek_date = to_workweek_date(date) # <--- new workweek_date = to_workweek_date(kwargs["date"]) # <--- new
return { return {
"date": workweek_date, # <--- changed "date": workweek_date, # <--- changed
"extra_class": extra_class, "extra_class": kwargs.get("extra_class", "text-blue"),
} }
``` ```
### 4. Pass inputs to components ### 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: the parametrized version of the component:
```htmldjango ```htmldjango
@ -224,7 +227,7 @@ Next, you will learn [how to use slots give your components even more flexibilit
### 5. Add defaults ### 5. Add defaults
In our example, we've set the `extra_class` to default to `"text-blue"` by setting it in the 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. method.
However, you may want to use the same default value in multiple methods, like However, you may want to use the same default value in multiple methods, like
@ -248,10 +251,10 @@ class Calendar(Component):
class Defaults: # <--- new class Defaults: # <--- new
extra_class = "text-blue" extra_class = "text-blue"
def get_context_data(self, date: date, extra_class: str): # <--- changed def get_template_data(self, args, kwargs, slots, context):
workweek_date = to_workweek_date(date) workweek_date = to_workweek_date(kwargs["date"])
return { return {
"date": workweek_date, "date": workweek_date,
"extra_class": extra_class, "extra_class": kwargs["extra_class"], # <--- changed
} }
``` ```

View file

@ -19,7 +19,7 @@ class Calendar(Component):
js_file = "calendar.js" js_file = "calendar.js"
css_file = "calendar.css" css_file = "calendar.css"
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": "1970-01-01", "date": "1970-01-01",
} }

View file

@ -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 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. when creating an instance of this component.
!!! note !!! 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. 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 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 }}`. will become available within the template as variables, e.g. as `{{ date }}`.
@ -152,7 +152,7 @@ from django_components import Component
class Calendar(Component): class Calendar(Component):
template_file = "calendar.html" template_file = "calendar.html"
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": "1970-01-01", "date": "1970-01-01",
} }

View file

@ -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: 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 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()`. 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()`.

View file

@ -53,8 +53,8 @@ class Calendar(Component):
js_file = "calendar.js" js_file = "calendar.js"
css_file = "calendar.css" css_file = "calendar.css"
def get_context_data(self, date): def get_template_data(self, args, kwargs, slots, context):
return {"date": date} return {"date": kwargs["date"]}
``` ```
Use the component like this: Use the component like this:
@ -115,9 +115,9 @@ class Calendar(Component):
css = ["bootstrap/dist/css/bootstrap.min.css"] css = ["bootstrap/dist/css/bootstrap.min.css"]
# Variables available in the template # Variables available in the template
def get_context_data(self, date): def get_template_data(self, args, kwargs, slots, context):
return { return {
"date": date "date": kwargs["date"]
} }
``` ```
@ -222,7 +222,7 @@ class Table(Component):
</div> </div>
""" """
def get_context_data(self, var1, var2, variable, another, **attrs): def get_template_data(self, args, kwargs, slots, context):
# Access component's ID # Access component's ID
assert self.id == "djc1A2b3c" assert self.id == "djc1A2b3c"
@ -237,7 +237,7 @@ class Table(Component):
assert self.context_processors_data['user'].username == "admin" assert self.context_processors_data['user'].username == "admin"
return { return {
"variable": variable, "variable": kwargs["variable"],
} }
# Access component's HTML / JS / CSS # 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 { return {
"page": page, "page": kwargs["page"],
} }
# Get auto-generated URL for the component # 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): class Header(Component):
template = "..." template = "..."
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
theme = self.inject("theme").variant theme = self.inject("theme").variant
return { return {
"theme": theme, "theme": theme,

View file

@ -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> </div>
""" """
def get_context_data(self, user: User): def get_template_data(self, args, kwargs, slots, context):
return { return {
"user": user, "user": kwargs["user"],
} }
``` ```
@ -381,7 +381,7 @@ class Child(Component):
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
user = self.inject("user_data").user user = self.inject("user_data").user
return { return {
"user": user, "user": user,

View file

@ -1,3 +1,5 @@
from typing import NamedTuple
from django_components import Component, register from django_components import Component, register
@ -13,9 +15,12 @@ class Calendar(Component):
js_file = "calendar/calendar.js" js_file = "calendar/calendar.js"
# This component takes one parameter, a date string to show in the template # This component takes one parameter, a date string to show in the template
def get_context_data(self, date): class Kwargs(NamedTuple):
date: str
def get_template_data(self, args, kwargs: Kwargs, slots, context):
return { return {
"date": date, "date": kwargs.date,
} }
class View: class View:
@ -41,9 +46,12 @@ class CalendarRelative(Component):
js_file = "calendar.js" js_file = "calendar.js"
# This component takes one parameter, a date string to show in the template # This component takes one parameter, a date string to show in the template
def get_context_data(self, date): class Kwargs(NamedTuple):
date: str
def get_template_data(self, args, kwargs: Kwargs, slots, context):
return { return {
"date": date, "date": kwargs.date,
} }
class View: class View:

View file

@ -1,24 +1,8 @@
from typing import Any, Dict
from django_components import Component, register, types from django_components import Component, register, types
@register("greeting") @register("greeting")
class Greeting(Component): class Greeting(Component):
class View:
def get(self, request, *args, **kwargs):
slots = {"message": "Hello, world!"}
return Greeting.render_to_response(
request=request,
slots=slots,
kwargs={
"name": request.GET.get("name", ""),
},
)
def get_context_data(self, name, *args, **kwargs) -> Dict[str, Any]:
return {"name": name}
template: types.django_html = """ template: types.django_html = """
<div id="greeting">Hello, {{ name }}!</div> <div id="greeting">Hello, {{ name }}!</div>
{% slot "message" %}{% endslot %} {% slot "message" %}{% endslot %}
@ -37,3 +21,17 @@ class Greeting(Component):
alert("Hello!"); alert("Hello!");
}); });
""" """
def get_template_data(self, args, kwargs, slots, context):
return {"name": kwargs["name"]}
class View:
def get(self, request, *args, **kwargs):
slots = {"message": "Hello, world!"}
return Greeting.render_to_response(
request=request,
slots=slots,
kwargs={
"name": request.GET.get("name", ""),
},
)

View file

@ -1,3 +1,5 @@
from typing import NamedTuple
from django_components import Component, register from django_components import Component, register
@ -13,9 +15,12 @@ class CalendarNested(Component):
js_file = "calendar.js" js_file = "calendar.js"
# This component takes one parameter, a date string to show in the template # This component takes one parameter, a date string to show in the template
def get_context_data(self, date): class Kwargs(NamedTuple):
date: str
def get_template_data(self, args, kwargs: Kwargs, slots, context):
return { return {
"date": date, "date": kwargs.date,
} }
class View: class View:

View file

@ -1,27 +1,11 @@
import time import time
from typing import Any, Dict from typing import NamedTuple
from django_components import Component, register, types from django_components import Component, register, types
@register("recursive") @register("recursive")
class Recursive(Component): class Recursive(Component):
class View:
def get(self, request):
time_before = time.time()
output = Recursive.render_to_response(
request=request,
kwargs={
"depth": 0,
},
)
time_after = time.time()
print("TIME: ", time_after - time_before)
return output
def get_context_data(self, depth: int = 0) -> Dict[str, Any]:
return {"depth": depth + 1}
template: types.django_html = """ template: types.django_html = """
<div id="recursive"> <div id="recursive">
depth: {{ depth }} depth: {{ depth }}
@ -31,3 +15,25 @@ class Recursive(Component):
{% endif %} {% endif %}
</div> </div>
""" """
class Kwargs(NamedTuple):
depth: int
class Defaults:
depth = 0
def get_template_data(self, args, kwargs: Kwargs, slots, context):
return {"depth": kwargs.depth + 1}
class View:
def get(self, request):
time_before = time.time()
output = Recursive.render_to_response(
request=request,
kwargs=Recursive.Kwargs(
depth=0,
),
)
time_after = time.time()
print("TIME: ", time_after - time_before)
return output

View file

@ -56,7 +56,7 @@ class ContextBehavior(str, Enum):
That is, they enrich the context, and pass it along. That is, they enrich the context, and pass it along.
1. Component fills use the context of the component they are within. 1. Component fills use the context of the component they are within.
2. Variables from [`Component.get_context_data()`](../api#django_components.Component.get_context_data) 2. Variables from [`Component.get_template_data()`](../api#django_components.Component.get_template_data)
are available to the component fill. are available to the component fill.
**Example:** **Example:**
@ -71,7 +71,7 @@ class ContextBehavior(str, Enum):
{% endwith %} {% endwith %}
``` ```
and this context returned from the `Component.get_context_data()` method and this context returned from the `Component.get_template_data()` method
```python ```python
{ "my_var": 123 } { "my_var": 123 }
``` ```
@ -98,7 +98,7 @@ class ContextBehavior(str, Enum):
""" """
This setting makes the component fills behave similar to Vue or React, where This setting makes the component fills behave similar to Vue or React, where
the fills use EXCLUSIVELY the context variables defined in the fills use EXCLUSIVELY the context variables defined in
[`Component.get_context_data()`](../api#django_components.Component.get_context_data). [`Component.get_template_data()`](../api#django_components.Component.get_template_data).
**Example:** **Example:**
@ -112,7 +112,7 @@ class ContextBehavior(str, Enum):
{% endwith %} {% endwith %}
``` ```
and this context returned from the `get_context_data()` method and this context returned from the `get_template_data()` method
```python ```python
{ "my_var": 123 } { "my_var": 123 }
``` ```

View file

@ -199,16 +199,20 @@ class CreateCommand(ComponentCommand):
@register("{name}") @register("{name}")
class {name.capitalize()}(Component): class {name.capitalize()}(Component):
template_file = "{name}/{template_filename}" template_file = "{template_filename}"
js_file = "{js_filename}"
css_file = "{css_filename}"
def get_context_data(self, value): class Kwargs(NamedTuple):
param: str
class Defaults:
param = "sample value"
def get_template_data(self, args, kwargs: Kwargs, slots, context):
return {{ return {{
"param": "sample value", "param": kwargs.param,
}} }}
class Media:
css = "{name}/{css_filename}"
js = "{name}/{js_filename}"
""" """
) )
f.write(py_content.strip()) f.write(py_content.strip())

View file

@ -231,9 +231,9 @@ class ComponentVars(NamedTuple):
```py ```py
class MyTable(Component): class MyTable(Component):
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"my_slot_filled": "my_slot" in self.input.slots "my_slot_filled": "my_slot" in slots
} }
``` ```
""" """
@ -508,7 +508,7 @@ class Component(metaclass=ComponentMeta):
class MyComponent(Component): class MyComponent(Component):
template_file = "path/to/template.html" template_file = "path/to/template.html"
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return {"name": "World"} return {"name": "World"}
``` ```
""" """
@ -556,7 +556,7 @@ class Component(metaclass=ComponentMeta):
class MyComponent(Component): class MyComponent(Component):
template = "Hello, {{ name }}!" template = "Hello, {{ name }}!"
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return {"name": "World"} return {"name": "World"}
``` ```
""" """
@ -1663,7 +1663,7 @@ class Component(metaclass=ComponentMeta):
```py ```py
class MyComponent(Component): class MyComponent(Component):
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
print(f"Rendering '{self.id}'") print(f"Rendering '{self.id}'")
return {} return {}
@ -1702,14 +1702,9 @@ class Component(metaclass=ComponentMeta):
**Example:** **Example:**
Use can use [`self.input.args`](../api/#django_components.ComponentInput.args)
and [`self.input.kwargs`](../api/#django_components.ComponentInput.kwargs)
to access the positional and keyword arguments passed to
[`Component.render()`](../api/#django_components.Component.render).
```python ```python
class Table(Component): 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 # Access component's inputs, slots and context
assert self.input.args == [123, "str"] assert self.input.args == [123, "str"]
assert self.input.kwargs == {"variable": "test", "another": 1} assert self.input.kwargs == {"variable": "test", "another": 1}
@ -1778,7 +1773,7 @@ class Component(metaclass=ComponentMeta):
```py ```py
class MyComponent(Component): class MyComponent(Component):
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
user_id = self.request.GET['user_id'] user_id = self.request.GET['user_id']
return { return {
'user_id': user_id, 'user_id': user_id,
@ -1801,7 +1796,7 @@ class Component(metaclass=ComponentMeta):
This data is also available from within the component's template, without having to This data is also available from within the component's template, without having to
return this data from return this data from
[`get_context_data()`](../api#django_components.Component.get_context_data). [`get_template_data()`](../api#django_components.Component.get_template_data).
In regular Django templates, you need to use In regular Django templates, you need to use
[`RequestContext`](https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.RequestContext) [`RequestContext`](https://docs.djangoproject.com/en/5.1/ref/templates/api/#django.template.RequestContext)
@ -1831,7 +1826,7 @@ class Component(metaclass=ComponentMeta):
```py ```py
class MyComponent(Component): class MyComponent(Component):
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
user = self.context_processors_data['user'] user = self.context_processors_data['user']
return { return {
'is_logged_in': user.is_authenticated, 'is_logged_in': user.is_authenticated,
@ -2371,7 +2366,7 @@ class Component(metaclass=ComponentMeta):
# By adding the current input to the stack, we temporarily allow users # By adding the current input to the stack, we temporarily allow users
# to access the provided context, slots, etc. Also required so users can # to access the provided context, slots, etc. Also required so users can
# call `self.inject()` from within `get_context_data()`. # call `self.inject()` from within `get_template_data()`.
# #
# This is handled as a stack, as users can potentially call `component.render()` # This is handled as a stack, as users can potentially call `component.render()`
# from within component hooks. Thus, then they do so, `component.id` will be the ID # from within component hooks. Thus, then they do so, `component.id` will be the ID

View file

@ -539,7 +539,7 @@ def _resolve_media(comp_cls: Type["Component"], comp_media: ComponentMedia) -> N
class MyComponent(Component): class MyComponent(Component):
media_class = MyMedia media_class = MyMedia
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
assert isinstance(self.media, MyMedia) assert isinstance(self.media, MyMedia)
``` ```
""" """

View file

@ -99,13 +99,14 @@ class DynamicComponent(Component):
_is_dynamic_component = True _is_dynamic_component = True
def get_context_data( def get_template_data(
self, self,
*args: Any, args: Any,
registry: Optional[ComponentRegistry] = None, kwargs: Any,
**kwargs: Any, slots: Any,
context: Any,
) -> Dict: ) -> Dict:
# NOTE: We have to access `is` via kwargs, because it's a special keyword in Python registry: Optional[ComponentRegistry] = kwargs.pop("registry", None)
comp_name_or_class: Union[str, Type[Component]] = kwargs.pop("is", None) comp_name_or_class: Union[str, Type[Component]] = kwargs.pop("is", None)
if not comp_name_or_class: if not comp_name_or_class:
raise TypeError(f"Component '{self.name}' is missing a required argument 'is'") raise TypeError(f"Component '{self.name}' is missing a required argument 'is'")

View file

@ -172,7 +172,7 @@ class ComponentExtension:
class MyExtension: class MyExtension:
... ...
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"my_extension": self.my_extension.do_something(), "my_extension": self.my_extension.do_something(),
} }
@ -212,7 +212,7 @@ class ComponentExtension:
class MyExtension: class MyExtension:
... ...
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"my_extension": self.my_extension.do_something(), "my_extension": self.my_extension.do_something(),
} }
@ -450,7 +450,7 @@ class ComponentExtension:
Use this hook to modify or validate component inputs before they're processed. Use this hook to modify or validate component inputs before they're processed.
This is the first hook that is called when rendering a component. As such this hook is called before This is the first hook that is called when rendering a component. As such this hook is called before
[`Component.get_context_data()`](../api#django_components.Component.get_context_data), [`Component.get_template_data()`](../api#django_components.Component.get_template_data),
[`Component.get_js_data()`](../api#django_components.Component.get_js_data), [`Component.get_js_data()`](../api#django_components.Component.get_js_data),
and [`Component.get_css_data()`](../api#django_components.Component.get_css_data) methods, and [`Component.get_css_data()`](../api#django_components.Component.get_css_data) methods,
and the and the
@ -482,7 +482,7 @@ class ComponentExtension:
after a component's context and data methods have been processed. after a component's context and data methods have been processed.
This hook is called after This hook is called after
[`Component.get_context_data()`](../api#django_components.Component.get_context_data), [`Component.get_template_data()`](../api#django_components.Component.get_template_data),
[`Component.get_js_data()`](../api#django_components.Component.get_js_data) [`Component.get_js_data()`](../api#django_components.Component.get_js_data)
and [`Component.get_css_data()`](../api#django_components.Component.get_css_data). and [`Component.get_css_data()`](../api#django_components.Component.get_css_data).
@ -498,7 +498,7 @@ class ComponentExtension:
class MyExtension(ComponentExtension): class MyExtension(ComponentExtension):
def on_component_data(self, ctx: OnComponentDataContext) -> None: def on_component_data(self, ctx: OnComponentDataContext) -> None:
# Add extra template variable to all components when they are rendered # Add extra template variable to all components when they are rendered
ctx.context_data["my_template_var"] = "my_value" ctx.template_data["my_template_var"] = "my_value"
``` ```
""" """
pass pass

View file

@ -100,9 +100,9 @@ class NodeMeta(type):
# #
# ```py # ```py
# class MyComponent(Component): # class MyComponent(Component):
# def get_context_data(self, name: str, **kwargs: Any) -> str: # def get_template_data(self, args, kwargs, slots, context) -> str:
# return { # return {
# "name": name, # "name": kwargs.pop("name"),
# "attrs": kwargs, # "attrs": kwargs,
# } # }
# template = """ # template = """

View file

@ -41,9 +41,9 @@ class ProvideNode(BaseNode):
</div> </div>
\"\"\" \"\"\"
def get_context_data(self, user: User): def get_template_data(self, args, kwargs, slots, context):
return { return {
"user": user, "user": kwargs["user"],
} }
``` ```
@ -59,7 +59,7 @@ class ProvideNode(BaseNode):
</div> </div>
\"\"\" \"\"\"
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
user = self.inject("user_data").user user = self.inject("user_data").user
return { return {
"user": user, "user": user,
@ -138,7 +138,7 @@ def set_provided_context_var(
) -> str: ) -> str:
""" """
'Provide' given data under given key. In other words, this data can be retrieved '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 using `self.inject(key)` inside of `get_template_data()` method of components that
are nested inside the `{% provide %}` tag. are nested inside the `{% provide %}` tag.
""" """
# NOTE: We raise TemplateSyntaxError since this func should be called only from # NOTE: We raise TemplateSyntaxError since this func should be called only from

View file

@ -1083,8 +1083,8 @@ def _nodelist_to_slot_render_func(
# #
# And so we want to put the `extra_context` into the same layer that contains `_COMPONENT_CONTEXT_KEY`. # And so we want to put the `extra_context` into the same layer that contains `_COMPONENT_CONTEXT_KEY`.
# #
# HOWEVER, the layer with `_COMPONENT_CONTEXT_KEY` also contains user-defined data from `get_context_data()`. # HOWEVER, the layer with `_COMPONENT_CONTEXT_KEY` also contains user-defined data from `get_template_data()`.
# Data from `get_context_data()` should take precedence over `extra_context`. So we have to insert # Data from `get_template_data()` should take precedence over `extra_context`. So we have to insert
# the forloop variables BEFORE that. # the forloop variables BEFORE that.
index_of_last_component_layer = get_last_index(ctx.dicts, lambda d: _COMPONENT_CONTEXT_KEY in d) index_of_last_component_layer = get_last_index(ctx.dicts, lambda d: _COMPONENT_CONTEXT_KEY in d)
if index_of_last_component_layer is None: if index_of_last_component_layer is None:
@ -1096,8 +1096,8 @@ def _nodelist_to_slot_render_func(
# the following line can be removed. # the following line can be removed.
index_of_last_component_layer -= 1 index_of_last_component_layer -= 1
# Insert the `extra_context` layer BEFORE the layer that defines the variables from get_context_data. # Insert the `extra_context` layer BEFORE the layer that defines the variables from get_template_data.
# Thus, get_context_data will overshadow these on conflict. # Thus, get_template_data will overshadow these on conflict.
ctx.dicts.insert(index_of_last_component_layer, extra_context or {}) ctx.dicts.insert(index_of_last_component_layer, extra_context or {})
trace_component_msg("RENDER_NODELIST", component_name, component_id=None, slot_name=slot_name) trace_component_msg("RENDER_NODELIST", component_name, component_id=None, slot_name=slot_name)

View file

@ -23,5 +23,5 @@ class MultFileComponent(Component):
kwargs={"variable": "GET"}, kwargs={"variable": "GET"},
) )
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context) -> Dict[str, Any]:
return {"variable": variable} return {"variable": kwargs["variable"]}

View file

@ -27,5 +27,5 @@ class RelativeFileComponent(Component):
kwargs={"variable": "GET"}, kwargs={"variable": "GET"},
) )
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context) -> Dict[str, Any]:
return {"variable": variable} return {"variable": kwargs["variable"]}

View file

@ -32,5 +32,5 @@ class RelativeFileWithPathObjComponent(Component):
js = PathObj("relative_file_pathobj.js") js = PathObj("relative_file_pathobj.js")
css = PathObj("relative_file_pathobj.css") css = PathObj("relative_file_pathobj.css")
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context) -> Dict[str, Any]:
return {"variable": variable} return {"variable": kwargs["variable"]}

View file

@ -29,5 +29,5 @@ class SingleFileComponent(Component):
kwargs={"variable": "GET"}, kwargs={"variable": "GET"},
) )
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context) -> Dict[str, Any]:
return {"variable": variable} return {"variable": kwargs["variable"]}

View file

@ -12,5 +12,5 @@ class RelativeFileWithPathObjComponent(Component):
js = "staticfiles.js" js = "staticfiles.js"
css = "staticfiles.css" css = "staticfiles.css"
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context) -> Dict[str, Any]:
return {"variable": variable} return {"variable": kwargs["variable"]}

View file

@ -17,10 +17,13 @@ class SimpleComponent(Component):
globalThis.testSimpleComponent = 'kapowww!' globalThis.testSimpleComponent = 'kapowww!'
""" """
def get_context_data(self, variable, variable2="default"): class Defaults:
variable2 = "default"
def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs["variable2"],
} }
class Media: class Media:
@ -48,8 +51,8 @@ class SimpleComponentNested(Component):
globalThis.testSimpleComponentNested = 'bongo!' globalThis.testSimpleComponentNested = 'bongo!'
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} return {"variable": kwargs["variable"]}
class Media: class Media:
css = ["style.css", "style2.css"] css = ["style.css", "style2.css"]
@ -72,8 +75,8 @@ class OtherComponent(Component):
globalThis.testOtherComponent = 'wowzee!' globalThis.testOtherComponent = 'wowzee!'
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} return {"variable": kwargs["variable"]}
class Media: class Media:
css = "style.css" css = "style.css"

View file

@ -1,4 +1,4 @@
from typing import Any, Dict, Optional from typing import Optional
from django_components import Component, register from django_components import Component, register
@ -14,5 +14,5 @@ class AppLvlCompComponent(Component):
js = "app_lvl_comp.js" js = "app_lvl_comp.js"
css = "app_lvl_comp.css" css = "app_lvl_comp.css"
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} return {"variable": kwargs["variable"]}

View file

@ -1,5 +1,3 @@
from typing import Any, Dict
from django_components import Component, register from django_components import Component, register
@ -12,5 +10,5 @@ class AppLvlCompComponent(Component):
js = "app_lvl_comp.js" js = "app_lvl_comp.js"
css = "app_lvl_comp.css" css = "app_lvl_comp.css"
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} return {"variable": kwargs["variable"]}

View file

@ -1,5 +1,3 @@
from typing import Any, Dict
from django_components import Component, register from django_components import Component, register
@ -10,5 +8,5 @@ class AppLvlCompComponent(Component):
{{ variable }} {{ variable }}
""" """
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} return {"variable": kwargs["variable"]}

View file

@ -144,9 +144,9 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"attrs": attrs, "attrs": kwargs["attrs"],
"defaults": {"class": "override-me"}, "defaults": {"class": "override-me"},
} }
@ -172,9 +172,9 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"attrs": attrs, "attrs": kwargs["attrs"],
"defaults": {"class": "override-me"}, "defaults": {"class": "override-me"},
"class": "123 457", "class": "123 457",
} }
@ -197,9 +197,9 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"attrs": attrs, "attrs": kwargs["attrs"],
"defaults": {"class": "override-me"}, "defaults": {"class": "override-me"},
} }
@ -225,9 +225,9 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"attrs": attrs, "attrs": kwargs["attrs"],
"defaults": {"class": "override-me"}, "defaults": {"class": "override-me"},
} }
@ -253,10 +253,10 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"props": { "props": {
"attrs": attrs, "attrs": kwargs["attrs"],
"defaults": {"class": "override-me"}, "defaults": {"class": "override-me"},
"class": "added_class", "class": "added_class",
"data-id": 123, "data-id": 123,
@ -285,8 +285,8 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return {"attrs": attrs} return {"attrs": kwargs["attrs"]}
template = Template(self.template_str) template = Template(self.template_str)
rendered = template.render(Context({"class_var": "padding-top-8"})) rendered = template.render(Context({"class_var": "padding-top-8"}))
@ -318,8 +318,8 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return {"attrs": attrs} return {"attrs": kwargs["attrs"]}
template = Template(self.template_str) template = Template(self.template_str)
@ -346,8 +346,8 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return {"attrs": attrs} return {"attrs": kwargs["attrs"]}
template = Template(self.template_str) template = Template(self.template_str)
@ -367,8 +367,8 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return {"attrs": attrs} return {"attrs": kwargs["attrs"]}
template = Template(self.template_str) template = Template(self.template_str)
rendered = template.render(Context({"class_var": "padding-top-8"})) rendered = template.render(Context({"class_var": "padding-top-8"}))
@ -391,8 +391,8 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return {"attrs": attrs} return {"attrs": kwargs["attrs"]}
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -421,8 +421,8 @@ class TestHtmlAttrs:
</div> </div>
""" # noqa: E501 """ # noqa: E501
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return {"attrs": attrs} return {"attrs": kwargs["attrs"]}
template = Template(self.template_str) template = Template(self.template_str)
rendered = template.render(Context({"class_var": "padding-top-8"})) rendered = template.render(Context({"class_var": "padding-top-8"}))
@ -446,9 +446,9 @@ class TestHtmlAttrs:
</div> </div>
""" """
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"attrs": attrs, "attrs": kwargs["attrs"],
"defaults": {"class": "override-me"}, "defaults": {"class": "override-me"},
} }
@ -474,7 +474,7 @@ class TestHtmlAttrs:
</div> </div>
""" """
def get_context_data(self, *args, attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"attrs": None, "attrs": None,
"defaults": None, "defaults": None,

View file

@ -4,7 +4,7 @@ For tests focusing on the `component` tag, see `test_templatetags_component.py`
""" """
import re import re
from typing import Any, Dict, no_type_check from typing import Dict, no_type_check
import pytest import pytest
from django.conf import settings from django.conf import settings
@ -48,9 +48,9 @@ class CustomClient(Client):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# TODO_REMOVE_IN_V1 - Superseded by `self.get_template` in v1
@djc_test @djc_test
class TestComponentOldTemplateApi: class TestComponentLegacyApi:
# TODO_REMOVE_IN_V1 - Superseded by `self.get_template` in v1
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR) @djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_get_template_string(self, components_settings): def test_get_template_string(self, components_settings):
class SimpleComponent(Component): class SimpleComponent(Component):
@ -60,6 +60,31 @@ class TestComponentOldTemplateApi:
""" """
return content return content
def get_template_data(self, args, kwargs, slots, context):
return {
"variable": kwargs.get("variable", None),
}
class Media:
css = "style.css"
js = "script.js"
rendered = SimpleComponent.render(kwargs={"variable": "test"})
assertHTMLEqual(
rendered,
"""
Variable: <strong data-djc-id-ca1bc3e>test</strong>
""",
)
# TODO_REMOVE_IN_V2 - `get_context_data()` was superseded by `self.get_template_data`
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_get_context_data(self, components_settings):
class SimpleComponent(Component):
template = """
Variable: <strong>{{ variable }}</strong>
"""
def get_context_data(self, variable=None): def get_context_data(self, variable=None):
return { return {
"variable": variable, "variable": variable,
@ -95,9 +120,9 @@ class TestComponent:
Variable: <strong>{{ variable }}</strong> Variable: <strong>{{ variable }}</strong>
""" """
def get_context_data(self, variable=None): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs.get("variable", None),
} }
class Media: class Media:
@ -121,9 +146,9 @@ class TestComponent:
""" """
return content return content
def get_context_data(self, variable=None): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs.get("variable", None),
} }
class Media: class Media:
@ -143,9 +168,9 @@ class TestComponent:
class SimpleComponent(Component): class SimpleComponent(Component):
template_file = "simple_template.html" template_file = "simple_template.html"
def get_context_data(self, variable=None): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs.get("variable", None),
} }
class Media: class Media:
@ -165,9 +190,9 @@ class TestComponent:
class SimpleComponent(Component): class SimpleComponent(Component):
template_name = "simple_template.html" template_name = "simple_template.html"
def get_context_data(self, variable=None): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs.get("variable", None),
} }
class Media: class Media:
@ -214,12 +239,12 @@ class TestComponent:
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR) @djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_template_file_dynamic(self, components_settings): def test_template_file_dynamic(self, components_settings):
class SvgComponent(Component): class SvgComponent(Component):
def get_context_data(self, name, css_class="", title="", **attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.pop("name", None),
"css_class": css_class, "css_class": kwargs.pop("css_class", None),
"title": title, "title": kwargs.pop("title", None),
**attrs, **kwargs,
} }
def get_template_name(self, context): def get_template_name(self, context):
@ -241,9 +266,9 @@ class TestComponent:
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR) @djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_allows_to_return_template(self, components_settings): def test_allows_to_return_template(self, components_settings):
class TestComponent(Component): class TestComponent(Component):
def get_context_data(self, variable, **attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs.pop("variable", None),
} }
def get_template(self, context): def get_template(self, context):
@ -261,7 +286,7 @@ class TestComponent:
def test_input(self): def test_input(self):
class TestComponent(Component): class TestComponent(Component):
@no_type_check @no_type_check
def get_context_data(self, var1, var2, variable, another, **attrs): def get_template_data(self, args, kwargs, slots, context):
assert self.input.args == [123, "str"] assert self.input.args == [123, "str"]
assert self.input.kwargs == {"variable": "test", "another": 1} assert self.input.kwargs == {"variable": "test", "another": 1}
assert isinstance(self.input.context, Context) assert isinstance(self.input.context, Context)
@ -269,7 +294,7 @@ class TestComponent:
assert self.input.slots["my_slot"](Context(), None, None) == "MY_SLOT" assert self.input.slots["my_slot"](Context(), None, None) == "MY_SLOT"
return { return {
"variable": variable, "variable": kwargs["variable"],
} }
@no_type_check @no_type_check
@ -312,15 +337,15 @@ class TestComponent:
</main> </main>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
data = self.inject("my_provide") data = self.inject("my_provide")
data["data1"] # This should raise TypeError data["data1"] # This should raise TypeError
return {"data": data} return {"data": data}
@register("provider") @register("provider")
class Provider(Component): class Provider(Component):
def get_context_data(self, data: Any) -> Any: def get_template_data(self, args, kwargs, slots, context):
return {"data": data} return {"data": kwargs["data"]}
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -331,8 +356,8 @@ class TestComponent:
@register("parent") @register("parent")
class Parent(Component): class Parent(Component):
def get_context_data(self, data: Any) -> Any: def get_template_data(self, args, kwargs, slots, context):
return {"data": data} return {"data": kwargs["data"]}
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -387,7 +412,7 @@ class TestComponent:
class SimpleComponent(Component): class SimpleComponent(Component):
template = "Hello" template = "Hello"
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return None return None
assert SimpleComponent.render() == "Hello" assert SimpleComponent.render() == "Hello"
@ -412,11 +437,11 @@ class TestComponentRender:
{% endslot %} {% endslot %}
""" """
def get_context_data(self, the_arg2=None, *args, the_kwarg=None, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"the_arg2": the_arg2, "the_arg2": args[0] if args else None,
"the_kwarg": the_kwarg, "the_kwarg": kwargs.pop("the_kwarg", None),
"args": args, "args": args[1:],
"kwargs": kwargs, "kwargs": kwargs,
} }
@ -425,7 +450,7 @@ class TestComponentRender:
rendered, rendered,
""" """
the_arg2: None the_arg2: None
args: () args: []
the_kwarg: None the_kwarg: None
kwargs: {} kwargs: {}
--- ---
@ -456,12 +481,12 @@ class TestComponentRender:
{% endslot %} {% endslot %}
""" """
def get_context_data(self, the_arg, the_arg2=None, *args, the_kwarg, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"the_arg": the_arg, "the_arg": args[0],
"the_arg2": the_arg2, "the_arg2": args[1],
"the_kwarg": the_kwarg, "the_kwarg": kwargs.pop("the_kwarg", None),
"args": args, "args": args[2:],
"kwargs": kwargs, "kwargs": kwargs,
} }
@ -476,7 +501,7 @@ class TestComponentRender:
""" """
the_arg: one the_arg: one
the_arg2: two the_arg2: two
args: ('three',) args: ['three']
the_kwarg: test the_kwarg: test
kwargs: {'kw2': 'ooo'} kwargs: {'kw2': 'ooo'}
--- ---
@ -509,12 +534,12 @@ class TestComponentRender:
{% endslot %} {% endslot %}
""" """
def get_context_data(self, the_arg, the_arg2=None, *args, the_kwarg, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"the_arg": the_arg, "the_arg": args[0],
"the_arg2": the_arg2, "the_arg2": args[1],
"the_kwarg": the_kwarg, "the_kwarg": kwargs.pop("the_kwarg"),
"args": args, "args": args[2:],
"kwargs": kwargs, "kwargs": kwargs,
} }
@ -531,7 +556,7 @@ class TestComponentRender:
""" """
the_arg: one the_arg: one
the_arg2: two the_arg2: two
args: ('three',) args: ['three']
the_kwarg: test the_kwarg: test
kwargs: {'kw2': 'ooo'} kwargs: {'kw2': 'ooo'}
--- ---
@ -580,10 +605,10 @@ class TestComponentRender:
{% endslot %} {% endslot %}
""" """
def get_context_data(self, the_arg, the_kwarg=None, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"the_arg": the_arg, "the_arg": args[0],
"the_kwarg": the_kwarg, "the_kwarg": kwargs.pop("the_kwarg", None),
"kwargs": kwargs, "kwargs": kwargs,
} }
@ -592,7 +617,7 @@ class TestComponentRender:
# NOTE: Since the slot has access to the Context object, it should behave # NOTE: Since the slot has access to the Context object, it should behave
# the same way as it does in templates - when in "isolated" mode, then the # the same way as it does in templates - when in "isolated" mode, then the
# slot fill has access only to the "root" context, but not to the data of # slot fill has access only to the "root" context, but not to the data of
# get_context_data() of SimpleComponent. # get_template_data() of SimpleComponent.
if is_isolated: if is_isolated:
assert ctx.get("the_arg") is None assert ctx.get("the_arg") is None
assert ctx.get("the_kwarg") is None assert ctx.get("the_kwarg") is None
@ -725,8 +750,8 @@ class TestComponentRender:
</div> </div>
""" """
def get_context_data(self, *args, how: str, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return {"how": how} return {"how": kwargs.pop("how")}
class View(ComponentView): class View(ComponentView):
def get(self, request): def get(self, request):
@ -734,7 +759,7 @@ class TestComponentRender:
return self.component.render_to_response( return self.component.render_to_response(
context=RequestContext(self.request), context=RequestContext(self.request),
kwargs=self.component.get_context_data(how=how), kwargs={"how": how},
) )
client = CustomClient(urlpatterns=[path("test_thing/", Thing.as_view())]) client = CustomClient(urlpatterns=[path("test_thing/", Thing.as_view())])
@ -869,7 +894,7 @@ class TestComponentRender:
class TestComponent(Component): class TestComponent(Component):
template = "Variable: <strong>{{ id }}</strong>" template = "Variable: <strong>{{ id }}</strong>"
def get_context_data(self, **attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"id": self.id, "id": self.id,
} }
@ -885,7 +910,7 @@ class TestComponentRender:
class TestComponent(Component): class TestComponent(Component):
template = "Variable: <strong>{{ id }}</strong>" template = "Variable: <strong>{{ id }}</strong>"
def get_context_data(self, **attrs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"id": self.id, "id": self.id,
} }
@ -923,7 +948,7 @@ class TestComponentHook:
{% endcomponent %} {% endcomponent %}
""" """
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"args": args, "args": args,
"kwargs": kwargs, "kwargs": kwargs,
@ -943,7 +968,7 @@ class TestComponentHook:
assertHTMLEqual( assertHTMLEqual(
rendered, rendered,
""" """
args: () args: []
kwargs: {} kwargs: {}
--- ---
from_on_before: :) from_on_before: :)
@ -984,7 +1009,7 @@ class TestComponentHook:
{% endcomponent %} {% endcomponent %}
""" """
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"args": args, "args": args,
"kwargs": kwargs, "kwargs": kwargs,
@ -1006,7 +1031,7 @@ class TestComponentHook:
assertHTMLEqual( assertHTMLEqual(
captured_content, captured_content,
""" """
args: () args: []
kwargs: {} kwargs: {}
--- ---
from_on_after: from_on_after:
@ -1020,7 +1045,7 @@ class TestComponentHook:
assertHTMLEqual( assertHTMLEqual(
rendered, rendered,
""" """
args: () args: []
kwargs: {} kwargs: {}
--- ---
from_on_after: from_on_after:
@ -1060,7 +1085,7 @@ class TestComponentHook:
{% endcomponent %} {% endcomponent %}
""" """
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"args": args, "args": args,
"kwargs": kwargs, "kwargs": kwargs,
@ -1077,7 +1102,7 @@ class TestComponentHook:
assertHTMLEqual( assertHTMLEqual(
captured_content, captured_content,
""" """
args: () args: []
kwargs: {} kwargs: {}
--- ---
from_on_before: from_on_before:
@ -1092,7 +1117,7 @@ class TestComponentHook:
rendered, rendered,
""" """
Chocolate cookie recipe: Chocolate cookie recipe:
args: () args: []
kwargs: {} kwargs: {}
--- ---
from_on_before: from_on_before:
@ -1131,7 +1156,7 @@ class TestComponentHook:
{% endcomponent %} {% endcomponent %}
""" """
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
return { return {
"args": args, "args": args,
"kwargs": kwargs, "kwargs": kwargs,

View file

@ -1,5 +1,4 @@
import time import time
from typing import Any
from django.core.cache import caches from django.core.cache import caches
from django.template import Template from django.template import Template
@ -33,7 +32,7 @@ class TestComponentCache:
class Cache: class Cache:
enabled = True enabled = True
def get_context_data(self, **kwargs: Any): def get_template_data(self, args, kwargs, slots, context):
nonlocal did_call_get nonlocal did_call_get
did_call_get = True did_call_get = True
return {} return {}
@ -55,7 +54,7 @@ class TestComponentCache:
did_call_get = False did_call_get = False
component.render() component.render()
# get_context_data not called because the cache entry was returned # get_template_data not called because the cache entry was returned
assert not did_call_get assert not did_call_get
assert result == "Hello" assert result == "Hello"
@ -68,7 +67,7 @@ class TestComponentCache:
class Cache: class Cache:
enabled = False enabled = False
def get_context_data(self, **kwargs: Any): def get_template_data(self, args, kwargs, slots, context):
nonlocal did_call_get nonlocal did_call_get
did_call_get = True did_call_get = True
return {} return {}
@ -89,7 +88,7 @@ class TestComponentCache:
did_call_get = False did_call_get = False
result = component.render() result = component.render()
# get_context_data IS called because the cache is NOT used # get_template_data IS called because the cache is NOT used
assert did_call_get assert did_call_get
assert result == "Hello" assert result == "Hello"
@ -151,8 +150,8 @@ class TestComponentCache:
class Cache: class Cache:
enabled = True enabled = True
def get_context_data(self, input, **kwargs: Any): def get_template_data(self, args, kwargs, slots, context):
return {"input": input} return {"input": kwargs["input"]}
component = TestComponent() component = TestComponent()
component.render( component.render(
@ -200,7 +199,7 @@ class TestComponentCache:
# Custom hash method for args and kwargs # Custom hash method for args and kwargs
return "custom-args-and-kwargs" return "custom-args-and-kwargs"
def get_context_data(self, *args, **kwargs: Any): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
component = TestComponent() component = TestComponent()

View file

@ -1,5 +1,4 @@
from dataclasses import field from dataclasses import field
from typing import Any
from django.template import Context from django.template import Context
@ -25,25 +24,36 @@ class TestComponentDefaults:
extra = "extra" extra = "extra"
fn = lambda: "fn_as_val" # noqa: E731 fn = lambda: "fn_as_val" # noqa: E731
def get_context_data(self, arg1: Any, variable: Any, another: Any, **attrs: Any): def get_template_data(self, args, kwargs, slots, context):
nonlocal did_call_context nonlocal did_call_context
did_call_context = True did_call_context = True
# Check that args and slots are NOT affected by the defaults assert kwargs == {
assert self.input.args == [123] "variable": "test", # User-given
assert [*self.input.slots.keys()] == ["my_slot"] "another": 1, # Default because missing
assert self.input.slots["my_slot"](Context(), None, None) == "MY_SLOT" # type: ignore[arg-type] "extra": "extra", # Default because `None` was given
"fn": self.Defaults.fn, # Default because missing
}
assert self.input.kwargs == { assert self.input.kwargs == {
"variable": "test", # User-given "variable": "test", # User-given
"another": 1, # Default because missing "another": 1, # Default because missing
"extra": "extra", # Default because `None` was given "extra": "extra", # Default because `None` was given
"fn": self.Defaults.fn, # Default because missing "fn": self.Defaults.fn, # Default because missing
} }
# Check that args and slots are NOT affected by the defaults
assert args == [123]
assert [*slots.keys()] == ["my_slot"]
assert slots["my_slot"](Context(), None, None) == "MY_SLOT" # type: ignore[arg-type]
assert self.input.args == [123]
assert [*self.input.slots.keys()] == ["my_slot"]
assert self.input.slots["my_slot"](Context(), None, None) == "MY_SLOT" # type: ignore[arg-type]
assert isinstance(self.input.context, Context) assert isinstance(self.input.context, Context)
return { return {
"variable": variable, "variable": kwargs["variable"],
} }
TestComponent.render( TestComponent.render(
@ -64,10 +74,14 @@ class TestComponentDefaults:
variable = "test" variable = "test"
fn = Default(lambda: "fn_as_factory") fn = Default(lambda: "fn_as_factory")
def get_context_data(self, variable: Any, **attrs: Any): def get_template_data(self, args, kwargs, slots, context):
nonlocal did_call_context nonlocal did_call_context
did_call_context = True did_call_context = True
assert kwargs == {
"variable": "test", # User-given
"fn": "fn_as_factory", # Default because missing
}
assert self.input.kwargs == { assert self.input.kwargs == {
"variable": "test", # User-given "variable": "test", # User-given
"fn": "fn_as_factory", # Default because missing "fn": "fn_as_factory", # Default because missing
@ -75,7 +89,7 @@ class TestComponentDefaults:
assert isinstance(self.input.context, Context) assert isinstance(self.input.context, Context)
return { return {
"variable": variable, "variable": kwargs["variable"],
} }
TestComponent.render( TestComponent.render(
@ -94,10 +108,15 @@ class TestComponentDefaults:
variable = "test" variable = "test"
fn = field(default=lambda: "fn_as_factory") fn = field(default=lambda: "fn_as_factory")
def get_context_data(self, variable: Any, **attrs: Any): def get_template_data(self, args, kwargs, slots, context):
nonlocal did_call_context nonlocal did_call_context
did_call_context = True did_call_context = True
assert kwargs == {
"variable": "test", # User-given
# NOTE: NOT a factory, because it was set as `field(default=...)`
"fn": self.Defaults.fn.default, # type: ignore[attr-defined]
}
assert self.input.kwargs == { assert self.input.kwargs == {
"variable": "test", # User-given "variable": "test", # User-given
# NOTE: NOT a factory, because it was set as `field(default=...)` # NOTE: NOT a factory, because it was set as `field(default=...)`
@ -106,7 +125,7 @@ class TestComponentDefaults:
assert isinstance(self.input.context, Context) assert isinstance(self.input.context, Context)
return { return {
"variable": variable, "variable": kwargs["variable"],
} }
TestComponent.render( TestComponent.render(
@ -125,10 +144,15 @@ class TestComponentDefaults:
variable = "test" variable = "test"
fn = field(default_factory=lambda: "fn_as_factory") fn = field(default_factory=lambda: "fn_as_factory")
def get_context_data(self, variable: Any, **attrs: Any): def get_template_data(self, args, kwargs, slots, context):
nonlocal did_call_context nonlocal did_call_context
did_call_context = True did_call_context = True
assert kwargs == {
"variable": "test", # User-given
# NOTE: IS a factory, because it was set as `field(default_factory=...)`
"fn": "fn_as_factory", # Default because missing
}
assert self.input.kwargs == { assert self.input.kwargs == {
"variable": "test", # User-given "variable": "test", # User-given
# NOTE: IS a factory, because it was set as `field(default_factory=...)` # NOTE: IS a factory, because it was set as `field(default_factory=...)`
@ -137,7 +161,7 @@ class TestComponentDefaults:
assert isinstance(self.input.context, Context) assert isinstance(self.input.context, Context)
return { return {
"variable": variable, "variable": kwargs["variable"],
} }
TestComponent.render( TestComponent.render(

View file

@ -132,9 +132,9 @@ class TestMainMedia:
css_file = "style.css" css_file = "style.css"
js_file = "script.js" js_file = "script.js"
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
} }
registry.register("test", TestComponent) registry.register("test", TestComponent)
@ -231,10 +231,10 @@ class TestMainMedia:
Var2 (uppercased): <strong>{{ var2|upper }}</strong> Var2 (uppercased): <strong>{{ var2|upper }}</strong>
""" """
def get_context_data(self, var1=None, var2=None): def get_template_data(self, args, kwargs, slots, context):
return { return {
"var1": var1, "var1": kwargs["var1"],
"var2": var2, "var2": kwargs["var2"],
} }
rendered = FilteredComponent.render(kwargs={"var1": "test1", "var2": "test2"}) rendered = FilteredComponent.render(kwargs={"var1": "test1", "var2": "test2"})
@ -871,7 +871,7 @@ class TestMediaRelativePath:
</div> </div>
""" # noqa """ # noqa
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return {"shadowing_variable": "NOT SHADOWED"} return {"shadowing_variable": "NOT SHADOWED"}
class VariableDisplay(Component): class VariableDisplay(Component):
@ -881,12 +881,12 @@ class TestMediaRelativePath:
<h1>Uniquely named variable = {{ unique_variable }}</h1> <h1>Uniquely named variable = {{ unique_variable }}</h1>
""" """
def get_context_data(self, shadowing_variable=None, new_variable=None): def get_template_data(self, args, kwargs, slots, context):
context = {} context = {}
if shadowing_variable is not None: if kwargs["shadowing_variable"] is not None:
context["shadowing_variable"] = shadowing_variable context["shadowing_variable"] = kwargs["shadowing_variable"]
if new_variable is not None: if kwargs["new_variable"] is not None:
context["unique_variable"] = new_variable context["unique_variable"] = kwargs["new_variable"]
return context return context
# Settings required for autodiscover to work # Settings required for autodiscover to work

View file

@ -142,7 +142,7 @@ class TestComponentTyping:
css_data_instance = None css_data_instance = None
class Button(Component): class Button(Component):
# Data returned from `get_context_data` # Data returned from `get_template_data`
@dataclass @dataclass
class TemplateData: class TemplateData:
data1: str data1: str
@ -232,7 +232,7 @@ class TestComponentTyping:
css_data_instance = None css_data_instance = None
class Button(Component): class Button(Component):
# Data returned from `get_context_data` # Data returned from `get_template_data`
@dataclass @dataclass
class TemplateData: class TemplateData:
data1: str data1: str
@ -348,7 +348,7 @@ class TestComponentTyping:
# The generic specifies the data available to the slot function # The generic specifies the data available to the slot function
footer: NotRequired[Slot[ButtonFooterSlotData]] footer: NotRequired[Slot[ButtonFooterSlotData]]
# Data returned from `get_context_data` # Data returned from `get_template_data`
class TemplateData(NamedTuple): class TemplateData(NamedTuple):
data1: str data1: str
data2: int data2: int

View file

@ -1,4 +1,4 @@
from typing import Any, Dict from typing import Any
import pytest import pytest
from django.conf import settings from django.conf import settings
@ -60,8 +60,8 @@ class TestComponentAsView(SimpleTestCase):
</form> </form>
""" """
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} return {"variable": kwargs["variable"]}
def render_template_view(request): def render_template_view(request):
template = Template( template = Template(
@ -90,8 +90,8 @@ class TestComponentAsView(SimpleTestCase):
</form> </form>
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"inner_var": variable} return {"inner_var": kwargs["variable"]}
class View(ComponentView): class View(ComponentView):
def get(self, request, *args, **kwargs) -> HttpResponse: def get(self, request, *args, **kwargs) -> HttpResponse:
@ -115,8 +115,8 @@ class TestComponentAsView(SimpleTestCase):
</form> </form>
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"inner_var": variable} return {"inner_var": kwargs["variable"]}
def get(self, request, *args, **kwargs) -> HttpResponse: def get(self, request, *args, **kwargs) -> HttpResponse:
return self.render_to_response(kwargs={"variable": "GET"}) return self.render_to_response(kwargs={"variable": "GET"})
@ -139,8 +139,8 @@ class TestComponentAsView(SimpleTestCase):
</form> </form>
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"inner_var": variable} return {"inner_var": kwargs["variable"]}
class View(ComponentView): class View(ComponentView):
def post(self, request, *args, **kwargs) -> HttpResponse: def post(self, request, *args, **kwargs) -> HttpResponse:
@ -165,8 +165,8 @@ class TestComponentAsView(SimpleTestCase):
</form> </form>
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"inner_var": variable} return {"inner_var": kwargs["variable"]}
def post(self, request, *args, **kwargs) -> HttpResponse: def post(self, request, *args, **kwargs) -> HttpResponse:
variable = request.POST.get("variable") variable = request.POST.get("variable")
@ -188,8 +188,8 @@ class TestComponentAsView(SimpleTestCase):
</form> </form>
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"inner_var": variable} return {"inner_var": kwargs["variable"]}
def get(self, request, *args, **kwargs) -> HttpResponse: def get(self, request, *args, **kwargs) -> HttpResponse:
return self.render_to_response(kwargs={"variable": self.name}) return self.render_to_response(kwargs={"variable": self.name})

View file

@ -24,8 +24,8 @@ class SimpleComponent(Component):
Variable: <strong>{{ variable }}</strong> Variable: <strong>{{ variable }}</strong>
""" """
def get_context_data(self, variable=None): def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} if variable is not None else {} return {"variable": kwargs.get("variable", None)} if "variable" in kwargs else {}
class VariableDisplay(Component): class VariableDisplay(Component):
@ -35,12 +35,12 @@ class VariableDisplay(Component):
<h1>Uniquely named variable = {{ unique_variable }}</h1> <h1>Uniquely named variable = {{ unique_variable }}</h1>
""" """
def get_context_data(self, shadowing_variable=None, new_variable=None): def get_template_data(self, args, kwargs, slots, context):
context = {} context = {}
if shadowing_variable is not None: if kwargs["shadowing_variable"] is not None:
context["shadowing_variable"] = shadowing_variable context["shadowing_variable"] = kwargs["shadowing_variable"]
if new_variable is not None: if kwargs["new_variable"] is not None:
context["unique_variable"] = new_variable context["unique_variable"] = kwargs["new_variable"]
return context return context
@ -55,8 +55,8 @@ class IncrementerComponent(Component):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.call_count = 0 self.call_count = 0
def get_context_data(self, value=0): def get_template_data(self, args, kwargs, slots, context):
value = int(value) value = int(kwargs.get("value", 0))
if hasattr(self, "call_count"): if hasattr(self, "call_count"):
self.call_count += 1 self.call_count += 1
else: else:
@ -88,7 +88,7 @@ class TestContext:
</div> </div>
""" # noqa """ # noqa
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return {"shadowing_variable": "NOT SHADOWED"} return {"shadowing_variable": "NOT SHADOWED"}
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR) @djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
@ -231,8 +231,8 @@ class TestParentArgs:
</div> </div>
""" # noqa """ # noqa
def get_context_data(self, parent_value): def get_template_data(self, args, kwargs, slots, context):
return {"inner_parent_value": parent_value} return {"inner_parent_value": kwargs["parent_value"]}
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR) @djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_parent_args_can_be_drawn_from_context(self, components_settings): def test_parent_args_can_be_drawn_from_context(self, components_settings):
@ -463,7 +463,7 @@ class TestIsolatedContext:
registry.register(name="simple_component", component=SimpleComponent) registry.register(name="simple_component", component=SimpleComponent)
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}
{% component 'simple_component' variable only %}{% endcomponent %} {% component 'simple_component' variable=variable only %}{% endcomponent %}
""" """
template = Template(template_str) template = Template(template_str)
rendered = template.render(Context({"variable": "outer_value"})).strip() rendered = template.render(Context({"variable": "outer_value"})).strip()
@ -490,7 +490,7 @@ class TestIsolatedContextSetting:
registry.register(name="simple_component", component=SimpleComponent) registry.register(name="simple_component", component=SimpleComponent)
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}
{% component 'simple_component' variable %}{% endcomponent %} {% component 'simple_component' variable=variable %}{% endcomponent %}
""" """
template = Template(template_str) template = Template(template_str)
rendered = template.render(Context({"variable": "outer_value"})) rendered = template.render(Context({"variable": "outer_value"}))
@ -516,7 +516,7 @@ class TestIsolatedContextSetting:
registry.register(name="simple_component", component=SimpleComponent) registry.register(name="simple_component", component=SimpleComponent)
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}
{% component 'simple_component' variable %} {% component 'simple_component' variable=variable %}
{% endcomponent %} {% endcomponent %}
""" """
template = Template(template_str) template = Template(template_str)
@ -549,7 +549,7 @@ class TestContextProcessors:
class TestComponent(Component): class TestComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal inner_request nonlocal inner_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -585,7 +585,7 @@ class TestContextProcessors:
{% component "test_child" / %} {% component "test_child" / %}
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal parent_request nonlocal parent_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -596,7 +596,7 @@ class TestContextProcessors:
class TestChildComponent(Component): class TestChildComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data_child nonlocal context_processors_data_child
nonlocal child_request nonlocal child_request
context_processors_data_child = self.context_processors_data context_processors_data_child = self.context_processors_data
@ -635,7 +635,7 @@ class TestContextProcessors:
{% endcomponent %} {% endcomponent %}
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal parent_request nonlocal parent_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -646,7 +646,7 @@ class TestContextProcessors:
class TestChildComponent(Component): class TestChildComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data_child nonlocal context_processors_data_child
nonlocal child_request nonlocal child_request
context_processors_data_child = self.context_processors_data context_processors_data_child = self.context_processors_data
@ -680,7 +680,7 @@ class TestContextProcessors:
class TestComponent(Component): class TestComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal inner_request nonlocal inner_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -709,7 +709,7 @@ class TestContextProcessors:
{% component "test_child" / %} {% component "test_child" / %}
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal parent_request nonlocal parent_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -720,7 +720,7 @@ class TestContextProcessors:
class TestChildComponent(Component): class TestChildComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data_child nonlocal context_processors_data_child
nonlocal child_request nonlocal child_request
context_processors_data_child = self.context_processors_data context_processors_data_child = self.context_processors_data
@ -746,7 +746,7 @@ class TestContextProcessors:
class TestComponent(Component): class TestComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal inner_request nonlocal inner_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -774,7 +774,7 @@ class TestContextProcessors:
{% component "test_child" / %} {% component "test_child" / %}
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal parent_request nonlocal parent_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -785,7 +785,7 @@ class TestContextProcessors:
class TestChildComponent(Component): class TestChildComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data_child nonlocal context_processors_data_child
nonlocal child_request nonlocal child_request
context_processors_data_child = self.context_processors_data context_processors_data_child = self.context_processors_data
@ -811,7 +811,7 @@ class TestContextProcessors:
class TestComponent(Component): class TestComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal inner_request nonlocal inner_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -834,7 +834,7 @@ class TestContextProcessors:
class TestComponent(Component): class TestComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal inner_request nonlocal inner_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -857,7 +857,7 @@ class TestContextProcessors:
class TestComponent(Component): class TestComponent(Component):
template: types.django_html = """{% csrf_token %}""" template: types.django_html = """{% csrf_token %}"""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
nonlocal context_processors_data nonlocal context_processors_data
nonlocal inner_request nonlocal inner_request
context_processors_data = self.context_processors_data context_processors_data = self.context_processors_data
@ -902,7 +902,7 @@ class TestOuterContextProperty:
Variable: <strong>{{ variable }}</strong> Variable: <strong>{{ variable }}</strong>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return self.outer_context.flatten() # type: ignore[union-attr] return self.outer_context.flatten() # type: ignore[union-attr]
template_str: types.django_html = """ template_str: types.django_html = """

View file

@ -38,10 +38,10 @@ class SimpleComponent(Component):
console.log("xyz"); console.log("xyz");
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
class Media: class Media:
@ -551,7 +551,7 @@ class TestDependenciesStrategySimple:
console.log("Hello"); console.log("Hello");
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:
@ -573,7 +573,7 @@ class TestDependenciesStrategySimple:
console.log("xyz"); console.log("xyz");
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:
@ -711,7 +711,7 @@ class TestDependenciesStrategyPrepend:
console.log("Hello"); console.log("Hello");
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:
@ -733,7 +733,7 @@ class TestDependenciesStrategyPrepend:
console.log("xyz"); console.log("xyz");
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:
@ -868,7 +868,7 @@ class TestDependenciesStrategyAppend:
console.log("Hello"); console.log("Hello");
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:
@ -890,7 +890,7 @@ class TestDependenciesStrategyAppend:
console.log("xyz"); console.log("xyz");
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:

View file

@ -25,10 +25,10 @@ class SimpleComponent(Component):
Variable: <strong>{{ variable }}</strong> Variable: <strong>{{ variable }}</strong>
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
class Media: class Media:
@ -55,7 +55,7 @@ class SimpleComponentNested(Component):
console.log("Hello"); console.log("Hello");
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:
@ -78,7 +78,7 @@ class OtherComponent(Component):
console.log("xyz"); console.log("xyz");
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:
@ -91,7 +91,7 @@ class SimpleComponentWithSharedDependency(Component):
Variable: <strong>{{ variable }}</strong> Variable: <strong>{{ variable }}</strong>
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
class Media: class Media:

View file

@ -1,7 +1,7 @@
"""Catch-all for tests that use template tags and don't fit other files""" """Catch-all for tests that use template tags and don't fit other files"""
import re import re
from typing import Any, Dict from typing import Dict
import pytest import pytest
from django.template import Context, Template, TemplateSyntaxError from django.template import Context, Template, TemplateSyntaxError
@ -68,21 +68,15 @@ class TestDynamicExpr:
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data( def get_template_data(self, args, kwargs, slots, context):
self, captured["pos_var1"] = args[0]
pos_var1: Any, captured["bool_var"] = kwargs["bool_var"]
*args: Any, captured["list_var"] = kwargs["list_var"]
bool_var: bool,
list_var: Dict,
):
captured["pos_var1"] = pos_var1
captured["bool_var"] = bool_var
captured["list_var"] = list_var
return { return {
"pos_var1": pos_var1, "pos_var1": args[0],
"bool_var": bool_var, "bool_var": kwargs["bool_var"],
"list_var": list_var, "list_var": kwargs["list_var"],
} }
template: types.django_html = """ template: types.django_html = """
@ -135,24 +129,17 @@ class TestDynamicExpr:
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data( def get_template_data(self, args, kwargs, slots, context):
self, captured["pos_var1"] = args[0]
pos_var1: Any, captured["bool_var"] = kwargs["bool_var"]
*args: Any, captured["list_var"] = kwargs["list_var"]
bool_var: bool, captured["dict_var"] = kwargs["dict_var"]
list_var: Dict,
dict_var: Dict,
):
captured["pos_var1"] = pos_var1
captured["bool_var"] = bool_var
captured["list_var"] = list_var
captured["dict_var"] = dict_var
return { return {
"pos_var1": pos_var1, "pos_var1": args[0],
"bool_var": bool_var, "bool_var": kwargs["bool_var"],
"list_var": list_var, "list_var": kwargs["list_var"],
"dict_var": dict_var, "dict_var": kwargs["dict_var"],
} }
template: types.django_html = """ template: types.django_html = """
@ -209,24 +196,17 @@ class TestDynamicExpr:
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data( def get_template_data(self, args, kwargs, slots, context):
self, captured["pos_var1"] = args[0]
pos_var1: Any, captured["pos_var2"] = args[1]
pos_var2: Any, captured["bool_var"] = kwargs["bool_var"]
*args: Any, captured["list_var"] = kwargs["list_var"]
bool_var: bool,
list_var: Dict,
):
captured["pos_var1"] = pos_var1
captured["pos_var2"] = pos_var2
captured["bool_var"] = bool_var
captured["list_var"] = list_var
return { return {
"pos_var1": pos_var1, "pos_var1": args[0],
"pos_var2": pos_var2, "pos_var2": args[1],
"bool_var": bool_var, "bool_var": kwargs["bool_var"],
"list_var": list_var, "list_var": kwargs["list_var"],
} }
template: types.django_html = """ template: types.django_html = """
@ -281,26 +261,18 @@ class TestDynamicExpr:
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data( def get_template_data(self, args, kwargs, slots, context):
self, captured["pos_var1"] = args[0]
pos_var1: Any, captured["bool_var"] = kwargs["bool_var"]
pos_var2: Any, captured["list_var"] = kwargs["list_var"]
*args: Any, captured["dict_var"] = kwargs["dict_var"]
bool_var: bool,
list_var: Dict,
dict_var: Dict,
):
captured["pos_var1"] = pos_var1
captured["bool_var"] = bool_var
captured["list_var"] = list_var
captured["dict_var"] = dict_var
return { return {
"pos_var1": pos_var1, "pos_var1": args[0],
"pos_var2": pos_var2, "pos_var2": args[1],
"bool_var": bool_var, "bool_var": kwargs["bool_var"],
"list_var": list_var, "list_var": kwargs["list_var"],
"dict_var": dict_var, "dict_var": kwargs["dict_var"],
} }
template: types.django_html = """ template: types.django_html = """
@ -357,17 +329,11 @@ class TestDynamicExpr:
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data( def get_template_data(self, args, kwargs, slots, context):
self,
pos_var1: Any,
pos_var2: Any,
*args: Any,
bool_var: bool,
):
return { return {
"pos_var1": pos_var1, "pos_var1": args[0],
"pos_var2": pos_var2, "pos_var2": args[1],
"bool_var": bool_var, "bool_var": kwargs["bool_var"],
} }
template: types.django_html = """ template: types.django_html = """
@ -404,15 +370,10 @@ class TestDynamicExpr:
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data( def get_template_data(self, args, kwargs, slots, context):
self,
pos_var1: Any,
*args: Any,
bool_var: bool,
):
return { return {
"pos_var1": pos_var1, "pos_var1": args[0],
"bool_var": bool_var, "bool_var": kwargs["bool_var"],
} }
template: types.django_html = """ template: types.django_html = """
@ -461,17 +422,12 @@ class TestSpreadOperator:
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data( def get_template_data(self, args, kwargs, slots, context):
self,
pos_var1: Any,
*args: Any,
**kwargs: Any,
):
nonlocal captured nonlocal captured
captured = kwargs captured = kwargs
return { return {
"pos_var1": pos_var1, "pos_var1": args[0],
**kwargs, **kwargs,
} }
@ -531,7 +487,7 @@ class TestSpreadOperator:
def test_slot(self, components_settings): def test_slot(self, components_settings):
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"my_dict": { "my_dict": {
"attrs:@click": "() => {}", "attrs:@click": "() => {}",
@ -568,7 +524,7 @@ class TestSpreadOperator:
def test_fill(self, components_settings): def test_fill(self, components_settings):
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"my_dict": { "my_dict": {
"attrs:@click": "() => {}", "attrs:@click": "() => {}",
@ -618,7 +574,7 @@ class TestSpreadOperator:
def test_provide(self, components_settings): def test_provide(self, components_settings):
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
data = self.inject("test") data = self.inject("test")
return { return {
"attrs": data.attrs, "attrs": data.attrs,
@ -692,11 +648,7 @@ class TestSpreadOperator:
def test_later_spreads_do_not_overwrite_earlier(self, components_settings): def test_later_spreads_do_not_overwrite_earlier(self, components_settings):
@register("test") @register("test")
class SimpleComponent(Component): class SimpleComponent(Component):
def get_context_data( def get_template_data(self, args, kwargs, slots, context):
self,
*args: Any,
**kwargs: Any,
):
return { return {
**kwargs, **kwargs,
} }
@ -794,7 +746,7 @@ class TestSpreadOperator:
class SimpleComponent(Component): class SimpleComponent(Component):
template = "" template = ""
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
nonlocal captured nonlocal captured
captured = args, kwargs captured = args, kwargs
return {} return {}
@ -820,7 +772,7 @@ class TestSpreadOperator:
template.render(context) template.render(context)
assert captured == ( assert captured == (
("a", "b", "c", 1, 2, 3), ["a", "b", "c", 1, 2, 3],
{}, {},
) )
@ -858,7 +810,7 @@ class TestAggregateKwargs:
class Test(Component): class Test(Component):
template = "" template = ""
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
nonlocal captured nonlocal captured
captured = args, kwargs captured = args, kwargs
return {} return {}
@ -879,7 +831,7 @@ class TestAggregateKwargs:
template.render(Context({"class_var": "padding-top-8", "four": 4})) template.render(Context({"class_var": "padding-top-8", "four": 4}))
assert captured == ( assert captured == (
(), [],
{ {
"attrs": { "attrs": {
"@click.stop": "dispatch('click_event')", "@click.stop": "dispatch('click_event')",

View file

@ -116,8 +116,8 @@ def with_component_cls(on_created: Callable):
class TempComponent(Component): class TempComponent(Component):
template = "Hello {{ name }}!" template = "Hello {{ name }}!"
def get_context_data(self, name="World"): def get_template_data(self, args, kwargs, slots, context):
return {"name": name} return {"name": kwargs.get("name", "World")}
on_created() on_created()
@ -143,8 +143,8 @@ class TestExtension:
class TestAccessComp(Component): class TestAccessComp(Component):
template = "Hello {{ name }}!" template = "Hello {{ name }}!"
def get_context_data(self, arg1, arg2, name="World"): def get_template_data(self, args, kwargs, slots, context):
return {"name": name} return {"name": kwargs.get("name", "World")}
ext_class = TestAccessComp.TestExtension # type: ignore[attr-defined] ext_class = TestAccessComp.TestExtension # type: ignore[attr-defined]
assert issubclass(ext_class, ComponentExtension.ExtensionClass) assert issubclass(ext_class, ComponentExtension.ExtensionClass)
@ -240,8 +240,8 @@ class TestExtensionHooks:
class TestComponent(Component): class TestComponent(Component):
template = "Hello {{ name }}!" template = "Hello {{ name }}!"
def get_context_data(self, name="World"): def get_template_data(self, args, kwargs, slots, context):
return {"name": name} return {"name": kwargs.get("name", "World")}
registry.register("test_comp", TestComponent) registry.register("test_comp", TestComponent)
extension = cast(DummyExtension, app_settings.EXTENSIONS[3]) extension = cast(DummyExtension, app_settings.EXTENSIONS[3])
@ -268,13 +268,13 @@ class TestExtensionHooks:
class TestComponent(Component): class TestComponent(Component):
template = "Hello {{ name }}!" template = "Hello {{ name }}!"
def get_context_data(self, arg1, arg2, name="World"): def get_template_data(self, args, kwargs, slots, context):
return {"name": name} return {"name": kwargs.get("name", "World")}
def get_js_data(self, *args, **kwargs): def get_js_data(self, args, kwargs, slots, context):
return {"script": "console.log('Hello!')"} return {"script": "console.log('Hello!')"}
def get_css_data(self, *args, **kwargs): def get_css_data(self, args, kwargs, slots, context):
return {"style": "body { color: blue; }"} return {"style": "body { color: blue; }"}
# Render the component with some args and kwargs # Render the component with some args and kwargs

View file

@ -182,9 +182,9 @@ class TestMultipleComponentRegistries:
Slot: {% slot "default" default / %} Slot: {% slot "default" default / %}
""" """
def get_context_data(self, variable=None): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs.get("variable", None),
} }
registry_a.register("simple_a", SimpleComponent) registry_a.register("simple_a", SimpleComponent)

View file

@ -51,10 +51,10 @@ class TestTemplateSignal:
class InnerComponent(Component): class InnerComponent(Component):
template_file = "simple_template.html" template_file = "simple_template.html"
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
class Media: class Media:

View file

@ -2706,7 +2706,7 @@ class TestResolver:
@register("test") @register("test")
class Test(Component): class Test(Component):
def get_context_data(self, **kwargs): def get_template_data(self, args, kwargs, slots, context):
nonlocal captured nonlocal captured
captured = kwargs captured = kwargs
return {} return {}
@ -2771,7 +2771,7 @@ class TestResolver:
class Test(Component): class Test(Component):
template = "var" template = "var"
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
nonlocal captured nonlocal captured
captured = args, kwargs captured = args, kwargs
return {} return {}
@ -2783,7 +2783,7 @@ class TestResolver:
""" """
Template(template_str).render(Context({"myvar": "myval", "val2": [1, 2, 3]})) Template(template_str).render(Context({"myvar": "myval", "val2": [1, 2, 3]}))
assert captured == ((42, "myval"), {"key": "val", "key2": [1, 2, 3]}) assert captured == ([42, "myval"], {"key": "val", "key2": [1, 2, 3]})
def test_component_special_kwargs(self): def test_component_special_kwargs(self):
captured = None captured = None
@ -2792,7 +2792,7 @@ class TestResolver:
class Test(Component): class Test(Component):
template = "var" template = "var"
def get_context_data(self, *args, **kwargs): def get_template_data(self, args, kwargs, slots, context):
nonlocal captured nonlocal captured
captured = args, kwargs captured = args, kwargs
return {} return {}
@ -2805,7 +2805,7 @@ class TestResolver:
Template(template_str).render(Context({"date": 2024, "bzz": "fzz"})) Template(template_str).render(Context({"date": 2024, "bzz": "fzz"}))
assert captured == ( assert captured == (
tuple([]), [],
{ {
"date": 2024, "date": 2024,
"@lol": 2, "@lol": 2,

View file

@ -33,9 +33,9 @@ class TestTemplateCache:
""" """
return content return content
def get_context_data(self, variable=None): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs.get("variable", None),
} }
comp = SimpleComponent() comp = SimpleComponent()

View file

@ -215,8 +215,8 @@ class TestTemplateParser:
Slot: {% slot "content" default / %} Slot: {% slot "content" default / %}
""" """
def get_context_data(self, var: str) -> dict: def get_template_data(self, args, kwargs, slots, context):
return {"var": var} return {"var": kwargs["var"]}
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}

View file

@ -29,17 +29,17 @@ class TestMultilineTags:
Variable: <strong>{{ variable }}</strong> Variable: <strong>{{ variable }}</strong>
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
{% component {% component
"test_component" "test_component"
123 variable=123
variable2="abc" variable2="abc"
%} %}
{% endcomponent %} {% endcomponent %}
@ -58,9 +58,9 @@ class TestNestedTags:
Variable: <strong>{{ var }}</strong> Variable: <strong>{{ var }}</strong>
""" """
def get_context_data(self, var): def get_template_data(self, args, kwargs, slots, context):
return { return {
"var": var, "var": kwargs["var"],
} }
# See https://github.com/django-components/django-components/discussions/671 # See https://github.com/django-components/django-components/discussions/671

View file

@ -1,4 +1,5 @@
import re import re
from typing import NamedTuple
import pytest import pytest
from django.template import Context, Template, TemplateSyntaxError from django.template import Context, Template, TemplateSyntaxError
@ -25,8 +26,8 @@ class SlottedComponentWithContext(Component):
</custom-template> </custom-template>
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} return {"variable": kwargs["variable"]}
####################### #######################
@ -41,10 +42,10 @@ class TestComponentTemplateTag:
Variable: <strong>{{ variable }}</strong> Variable: <strong>{{ variable }}</strong>
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
class Media: class Media:
@ -114,10 +115,10 @@ class TestComponentTemplateTag:
{% endif %} {% endif %}
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
class Media: class Media:
@ -178,8 +179,11 @@ class TestComponentTemplateTag:
Default: <p>{{ default_param }}</p> Default: <p>{{ default_param }}</p>
""" """
def get_context_data(self, variable, default_param="default text"): def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable, "default_param": default_param} return {
"variable": kwargs["variable"],
"default_param": kwargs.get("default_param", "default text"),
}
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -204,10 +208,17 @@ class TestDynamicComponentTemplateTag:
Variable: <strong>{{ variable }}</strong> Variable: <strong>{{ variable }}</strong>
""" """
def get_context_data(self, variable, variable2="default"): class Kwargs(NamedTuple):
variable: str
variable2: str
class Defaults:
variable2 = "default"
def get_template_data(self, args, kwargs: Kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs.variable,
"variable2": variable2, "variable2": kwargs.variable2,
} }
class Media: class Media:
@ -375,10 +386,10 @@ class TestDynamicComponentTemplateTag:
Slot: {% slot "default" default / %} Slot: {% slot "default" default / %}
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
registry.register(name="test", component=SimpleSlottedComponent) registry.register(name="test", component=SimpleSlottedComponent)
@ -412,10 +423,10 @@ class TestDynamicComponentTemplateTag:
Slot 2: {% slot "two" / %} Slot 2: {% slot "two" / %}
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
registry.register(name="test", component=SimpleSlottedComponent) registry.register(name="test", component=SimpleSlottedComponent)
@ -455,10 +466,10 @@ class TestDynamicComponentTemplateTag:
Slot 2: {% slot "two" / %} Slot 2: {% slot "two" / %}
""" """
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
registry.register(name="test", component=SimpleSlottedComponent) registry.register(name="test", component=SimpleSlottedComponent)
@ -722,8 +733,11 @@ class TestAggregateInput:
</div> </div>
""" """
def get_context_data(self, *args, attrs, my_dict): def get_template_data(self, args, kwargs, slots, context):
return {"attrs": attrs, "my_dict": my_dict} return {
"attrs": kwargs["attrs"],
"my_dict": kwargs["my_dict"],
}
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -751,9 +765,12 @@ class TestRecursiveComponent:
@register("recursive") @register("recursive")
class Recursive(Component): class Recursive(Component):
def get_context_data(self, depth: int = 0): class Defaults:
print("depth:", depth) depth = 0
return {"depth": depth + 1, "DEPTH": DEPTH}
def get_template_data(self, args, kwargs, slots, context):
print("depth:", kwargs["depth"])
return {"depth": kwargs["depth"] + 1, "DEPTH": DEPTH}
template: types.django_html = """ template: types.django_html = """
<div> <div>

View file

@ -820,7 +820,7 @@ class TestExtendsCompat:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("block_provide") var = self.inject("block_provide")
return {"var": var} return {"var": var}

View file

@ -1,5 +1,4 @@
import re import re
from typing import Any
import pytest import pytest
from django.template import Context, Template, TemplateSyntaxError from django.template import Context, Template, TemplateSyntaxError
@ -29,7 +28,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -79,7 +78,7 @@ class TestProvideTemplateTag:
<div> another: {{ another }} </div> <div> another: {{ another }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
my_provide = self.inject("my_provide") my_provide = self.inject("my_provide")
return { return {
"key": my_provide.key, "key": my_provide.key,
@ -114,7 +113,7 @@ class TestProvideTemplateTag:
<div> another: {{ my_provide.another }} </div> <div> another: {{ my_provide.another }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
my_provide = self.inject("my_provide") my_provide = self.inject("my_provide")
return { return {
"my_provide": my_provide, "my_provide": my_provide,
@ -147,7 +146,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -179,7 +178,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -214,7 +213,7 @@ class TestProvideTemplateTag:
<div></div> <div></div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return {} return {}
template_str: types.django_html = """ template_str: types.django_html = """
@ -246,7 +245,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -279,7 +278,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -318,7 +317,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -361,7 +360,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -390,7 +389,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -419,7 +418,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -446,7 +445,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -478,7 +477,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -515,7 +514,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -558,7 +557,7 @@ class TestProvideTemplateTag:
<div> second_provide: {{ second_provide|safe }} </div> <div> second_provide: {{ second_provide|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
first_provide = self.inject("first_provide", "default") first_provide = self.inject("first_provide", "default")
second_provide = self.inject("second_provide", "default") second_provide = self.inject("second_provide", "default")
return { return {
@ -595,7 +594,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -626,7 +625,7 @@ class TestProvideTemplateTag:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide", "default") var = self.inject("my_provide", "default")
return {"var": var} return {"var": var}
@ -674,7 +673,7 @@ class TestInject:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("my_provide") var = self.inject("my_provide")
return {"var": var} return {"var": var}
@ -704,7 +703,7 @@ class TestInject:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("abc") var = self.inject("abc")
return {"var": var} return {"var": var}
@ -728,7 +727,7 @@ class TestInject:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("abc", "default") var = self.inject("abc", "default")
return {"var": var} return {"var": var}
@ -755,7 +754,7 @@ class TestInject:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("") var = self.inject("")
return {"var": var} return {"var": var}
@ -783,7 +782,7 @@ class TestInject:
<div> injected: {{ var|safe }} </div> <div> injected: {{ var|safe }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
var = self.inject("abc", "default") var = self.inject("abc", "default")
return {"var": var} return {"var": var}
@ -806,14 +805,14 @@ class TestInject:
</main> </main>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
data = self.inject("my_provide") data = self.inject("my_provide")
return {"data": data} return {"data": data}
@register("provider") @register("provider")
class Provider(Component): class Provider(Component):
def get_context_data(self, data: Any) -> Any: def get_template_data(self, args, kwargs, slots, context):
return {"data": data} return {"data": kwargs["data"]}
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -824,8 +823,8 @@ class TestInject:
@register("parent") @register("parent")
class Parent(Component): class Parent(Component):
def get_context_data(self, data: Any) -> Any: def get_template_data(self, args, kwargs, slots, context):
return {"data": data} return {"data": kwargs["data"]}
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -875,14 +874,14 @@ class TestInject:
</main> </main>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
data = self.inject("my_provide") data = self.inject("my_provide")
return {"data": data} return {"data": data}
@register("provider") @register("provider")
class Provider(Component): class Provider(Component):
def get_context_data(self, data: Any) -> Any: def get_template_data(self, args, kwargs, slots, context):
return {"data": data} return {"data": kwargs["data"]}
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -893,8 +892,8 @@ class TestInject:
@register("parent") @register("parent")
class Parent(Component): class Parent(Component):
def get_context_data(self, data: Any) -> Any: def get_template_data(self, args, kwargs, slots, context):
return {"data": data} return {"data": kwargs["data"]}
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -949,7 +948,7 @@ class TestProvideCache:
<div> Ran: {{ ran }} </div> <div> Ran: {{ ran }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
assert len(provide_cache) == 1 assert len(provide_cache) == 1
data = self.inject("my_provide") data = self.inject("my_provide")
@ -989,7 +988,7 @@ class TestProvideCache:
class Injectee(Component): class Injectee(Component):
template = "" template = ""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
assert len(provide_cache) == 1 assert len(provide_cache) == 1
data = self.inject("my_provide") data = self.inject("my_provide")
@ -1022,7 +1021,7 @@ class TestProvideCache:
<div> Ran: {{ ran }} </div> <div> Ran: {{ ran }} </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
assert len(provide_cache) == 1 assert len(provide_cache) == 1
data = self.inject("my_provide") data = self.inject("my_provide")
@ -1059,7 +1058,7 @@ class TestProvideCache:
class Injectee(Component): class Injectee(Component):
template = "" template = ""
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
assert len(provide_cache) == 1 assert len(provide_cache) == 1
data = self.inject("my_provide") data = self.inject("my_provide")

View file

@ -1,5 +1,5 @@
import re import re
from typing import Any, Dict, List, Optional from typing import Dict
import pytest import pytest
from django.template import Context, Template, TemplateSyntaxError from django.template import Context, Template, TemplateSyntaxError
@ -40,10 +40,10 @@ class TestComponentSlot:
class SimpleComponent(Component): class SimpleComponent(Component):
template = """Variable: <strong>{{ variable }}</strong>""" template = """Variable: <strong>{{ variable }}</strong>"""
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
class Media: class Media:
@ -96,10 +96,10 @@ class TestComponentSlot:
class SimpleComponent(Component): class SimpleComponent(Component):
template = """Variable: <strong>{{ variable }}</strong>""" template = """Variable: <strong>{{ variable }}</strong>"""
def get_context_data(self, variable, variable2="default"): def get_template_data(self, args, kwargs, slots, context):
return { return {
"variable": variable, "variable": kwargs["variable"],
"variable2": variable2, "variable2": kwargs.get("variable2", "default"),
} }
template_str: types.django_html = """ template_str: types.django_html = """
@ -152,8 +152,8 @@ class TestComponentSlot:
</custom-template> </custom-template>
""" """
def get_context_data(self, variable): def get_template_data(self, args, kwargs, slots, context):
return {"variable": variable} return {"variable": kwargs["variable"]}
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -294,9 +294,9 @@ class TestComponentSlot:
</div> </div>
""" """
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.get("name", None),
} }
registry.register("test", SlottedComponent) registry.register("test", SlottedComponent)
@ -451,8 +451,8 @@ class TestComponentSlot:
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR) @djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_multiple_slots_with_same_name_different_flags(self, components_settings): def test_multiple_slots_with_same_name_different_flags(self, components_settings):
class TestComp(Component): class TestComp(Component):
def get_context_data(self, required: bool) -> Any: def get_template_data(self, args, kwargs, slots, context):
return {"required": required} return {"required": kwargs["required"]}
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -824,8 +824,8 @@ class TestComponentSlotDefault:
def test_implicit_fill_when_slot_marked_default_not_rendered(self, components_settings): def test_implicit_fill_when_slot_marked_default_not_rendered(self, components_settings):
@register("test_comp") @register("test_comp")
class ConditionalSlotted(Component): class ConditionalSlotted(Component):
def get_context_data(self, var: bool) -> Any: def get_template_data(self, args, kwargs, slots, context):
return {"var": var} return {"var": kwargs["var"]}
template: types.django_html = """ template: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -885,9 +885,9 @@ class TestPassthroughSlots:
</custom-template> </custom-template>
""" """
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.get("name", None),
} }
template_str: types.django_html = """ template_str: types.django_html = """
@ -941,9 +941,9 @@ class TestPassthroughSlots:
</custom-template> </custom-template>
""" """
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.get("name", None),
} }
template_str: types.django_html = """ template_str: types.django_html = """
@ -985,9 +985,9 @@ class TestPassthroughSlots:
</custom-template> </custom-template>
""" """
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.get("name", None),
} }
template_str: types.django_html = """ template_str: types.django_html = """
@ -1021,7 +1021,7 @@ class TestPassthroughSlots:
def test_slots_inside_loops(self, components_settings): def test_slots_inside_loops(self, components_settings):
@register("test_comp") @register("test_comp")
class OuterComp(Component): class OuterComp(Component):
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"slots": ["header", "main", "footer"], "slots": ["header", "main", "footer"],
} }
@ -1064,7 +1064,7 @@ class TestPassthroughSlots:
@register("test_comp") @register("test_comp")
class OuterComp(Component): class OuterComp(Component):
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"slots": self.input.slots, "slots": self.input.slots,
} }
@ -1115,7 +1115,7 @@ class TestPassthroughSlots:
@register("test_comp") @register("test_comp")
class OuterComp(Component): class OuterComp(Component):
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"slots": self.input.slots, "slots": self.input.slots,
} }
@ -1510,7 +1510,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"var123": 456, "var123": 456,
@ -1545,7 +1545,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"var123": 456, "var123": 456,
@ -1580,7 +1580,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"var123": 456, "var123": 456,
@ -1617,7 +1617,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"slot_name": "my_slot", "slot_name": "my_slot",
"abc": "def", "abc": "def",
@ -1653,7 +1653,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"slot_props": { "slot_props": {
"name": "my_slot", "name": "my_slot",
@ -1692,7 +1692,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "xyz", "abc": "xyz",
"var123": 456, "var123": 456,
@ -1728,7 +1728,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"var123": 456, "var123": 456,
@ -1759,7 +1759,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"var123": 456, "var123": 456,
@ -1811,7 +1811,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"var123": 456, "var123": 456,
@ -1837,7 +1837,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"var123": 456, "var123": 456,
@ -1880,7 +1880,7 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"var123": 456, "var123": 456,
@ -1925,10 +1925,10 @@ class TestScopedSlot:
</div> </div>
""" """
def get_context_data(self, input): def get_template_data(self, args, kwargs, slots, context):
return { return {
"abc": "def", "abc": "def",
"input": input, "input": kwargs["input"],
} }
template_str: types.django_html = """ template_str: types.django_html = """
@ -1974,9 +1974,9 @@ class TestDuplicateSlot:
<footer>{% slot "footer" %}Default footer{% endslot %}</footer> <footer>{% slot "footer" %}Default footer{% endslot %}</footer>
""" """
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.get("name", None),
} }
return DuplicateSlotComponent return DuplicateSlotComponent
@ -2001,9 +2001,9 @@ class TestDuplicateSlot:
</div> </div>
""" """
def get_context_data(self, items: List) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"items": items, "items": kwargs["items"],
} }
return DuplicateSlotNestedComponent return DuplicateSlotNestedComponent
@ -2243,9 +2243,9 @@ class TestSlotBehavior:
</custom-template> </custom-template>
""" """
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.get("name", None),
} }
registry.register("test", SlottedComponent) registry.register("test", SlottedComponent)
@ -2360,7 +2360,7 @@ class TestSlotBehavior:
class TestSlotInput: class TestSlotInput:
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR) @djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_slots_accessible_when_python_render(self, components_settings): def test_slots_accessible_when_python_render(self, components_settings):
slots: Dict = {} seen_slots: Dict = {}
@register("test") @register("test")
class SlottedComponent(Component): class SlottedComponent(Component):
@ -2371,12 +2371,12 @@ class TestSlotInput:
<footer>{% slot "footer" %}Default footer{% endslot %}</footer> <footer>{% slot "footer" %}Default footer{% endslot %}</footer>
""" """
def get_context_data(self, input: Optional[int] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
nonlocal slots nonlocal seen_slots
slots = self.input.slots seen_slots = slots
return {} return {}
assert slots == {} assert seen_slots == {}
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}
@ -2390,14 +2390,14 @@ class TestSlotInput:
template = Template(template_str) template = Template(template_str)
template.render(Context()) template.render(Context())
assert list(slots.keys()) == ["header", "main"] assert list(seen_slots.keys()) == ["header", "main"]
assert callable(slots["header"]) assert callable(seen_slots["header"])
assert callable(slots["main"]) assert callable(seen_slots["main"])
assert "footer" not in slots assert "footer" not in seen_slots
@djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR) @djc_test(parametrize=PARAMETRIZE_CONTEXT_BEHAVIOR)
def test_slots_normalized_as_slot_instances(self, components_settings): def test_slots_normalized_as_slot_instances(self, components_settings):
slots: Dict[str, Slot] = {} seen_slots: Dict[str, Slot] = {}
@register("test") @register("test")
class SlottedComponent(Component): class SlottedComponent(Component):
@ -2408,12 +2408,12 @@ class TestSlotInput:
<footer>{% slot "footer" %}Default footer{% endslot %}</footer> <footer>{% slot "footer" %}Default footer{% endslot %}</footer>
""" """
def get_context_data(self, input: Optional[int] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
nonlocal slots nonlocal seen_slots
slots = self.input.slots seen_slots = slots
return {} return {}
assert slots == {} assert seen_slots == {}
header_slot = Slot(lambda *a, **kw: "HEADER_SLOT") header_slot = Slot(lambda *a, **kw: "HEADER_SLOT")
main_slot_str = "MAIN_SLOT" main_slot_str = "MAIN_SLOT"
@ -2427,11 +2427,11 @@ class TestSlotInput:
} }
) )
assert isinstance(slots["header"], Slot) assert isinstance(seen_slots["header"], Slot)
assert slots["header"](Context(), None, None) == "HEADER_SLOT" # type: ignore[arg-type] assert seen_slots["header"](Context(), None, None) == "HEADER_SLOT" # type: ignore[arg-type]
assert isinstance(slots["main"], Slot) assert isinstance(seen_slots["main"], Slot)
assert slots["main"](Context(), None, None) == "MAIN_SLOT" # type: ignore[arg-type] assert seen_slots["main"](Context(), None, None) == "MAIN_SLOT" # type: ignore[arg-type]
assert isinstance(slots["footer"], Slot) assert isinstance(seen_slots["footer"], Slot)
assert slots["footer"](Context(), None, None) == "FOOTER_SLOT" # type: ignore[arg-type] assert seen_slots["footer"](Context(), None, None) == "FOOTER_SLOT" # type: ignore[arg-type]

View file

@ -1,7 +1,5 @@
"""This file tests various ways how the individual tags can be combined inside the templates""" """This file tests various ways how the individual tags can be combined inside the templates"""
from typing import Any, Dict, Optional
from django.template import Context, Template from django.template import Context, Template
from pytest_django.asserts import assertHTMLEqual from pytest_django.asserts import assertHTMLEqual
@ -103,9 +101,9 @@ class TestNestedSlot:
</custom-template> </custom-template>
""" """
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.get("name", None),
} }
registry.clear() registry.clear()
@ -168,8 +166,8 @@ class TestConditionalSlot:
{% endif %} {% endif %}
""" """
def get_context_data(self, branch=None): def get_template_data(self, args, kwargs, slots, context):
return {"branch": branch} return {"branch": kwargs.get("branch", None)}
return ConditionalComponent return ConditionalComponent
@ -267,9 +265,9 @@ class TestSlotIteration:
{% endfor %} {% endfor %}
""" """
def get_context_data(self, objects, *args, **kwargs) -> dict: def get_template_data(self, args, kwargs, slots, context):
return { return {
"objects": objects, "objects": kwargs["objects"],
} }
return ComponentSimpleSlotInALoop return ComponentSimpleSlotInALoop
@ -303,7 +301,7 @@ class TestSlotIteration:
assertHTMLEqual(rendered, expected) assertHTMLEqual(rendered, expected)
# NOTE: Second arg in tuple is expected result. In isolated mode, while loops should NOT leak, # NOTE: Second arg in tuple is expected result. In isolated mode, while loops should NOT leak,
# we should still have access to root context (returned from get_context_data) # we should still have access to root context (returned from get_template_data)
@djc_test( @djc_test(
parametrize=( parametrize=(
["components_settings", "expected"], ["components_settings", "expected"],
@ -376,7 +374,7 @@ class TestSlotIteration:
assertHTMLEqual(rendered, expected) assertHTMLEqual(rendered, expected)
# NOTE: Second arg in tuple is expected result. In isolated mode, while loops should NOT leak, # NOTE: Second arg in tuple is expected result. In isolated mode, while loops should NOT leak,
# we should still have access to root context (returned from get_context_data) # we should still have access to root context (returned from get_template_data)
@djc_test( @djc_test(
parametrize=( parametrize=(
["components_settings", "expected"], ["components_settings", "expected"],
@ -662,9 +660,9 @@ class TestComponentNesting:
</custom-template> </custom-template>
""" """
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return { return {
"name": name, "name": kwargs.get("name", None),
} }
registry.register("test", SlottedComponent) registry.register("test", SlottedComponent)
@ -817,8 +815,8 @@ class TestComponentNesting:
{% endfor %} {% endfor %}
""" """
def get_context_data(self, items, *args, **kwargs) -> Dict[str, Any]: def get_template_data(self, args, kwargs, slots, context):
return {"items": items} return {"items": kwargs["items"]}
template_str: types.django_html = """ template_str: types.django_html = """
{% load component_tags %} {% load component_tags %}