mirror of
https://github.com/django-components/django-components.git
synced 2025-11-14 20:49:41 +00:00
feat: allow to set comp defaults on Kwargs class (#1467)
Some checks are pending
Docs - build & deploy / docs (push) Waiting to run
Run tests / build (ubuntu-latest, 3.10) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.11) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.12) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.13) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.8) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.9) (push) Waiting to run
Run tests / build (windows-latest, 3.10) (push) Waiting to run
Run tests / build (windows-latest, 3.11) (push) Waiting to run
Run tests / build (windows-latest, 3.12) (push) Waiting to run
Run tests / build (windows-latest, 3.13) (push) Waiting to run
Run tests / build (windows-latest, 3.8) (push) Waiting to run
Run tests / build (windows-latest, 3.9) (push) Waiting to run
Run tests / test_docs (3.13) (push) Waiting to run
Run tests / test_sampleproject (3.13) (push) Waiting to run
Some checks are pending
Docs - build & deploy / docs (push) Waiting to run
Run tests / build (ubuntu-latest, 3.10) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.11) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.12) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.13) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.8) (push) Waiting to run
Run tests / build (ubuntu-latest, 3.9) (push) Waiting to run
Run tests / build (windows-latest, 3.10) (push) Waiting to run
Run tests / build (windows-latest, 3.11) (push) Waiting to run
Run tests / build (windows-latest, 3.12) (push) Waiting to run
Run tests / build (windows-latest, 3.13) (push) Waiting to run
Run tests / build (windows-latest, 3.8) (push) Waiting to run
Run tests / build (windows-latest, 3.9) (push) Waiting to run
Run tests / test_docs (3.13) (push) Waiting to run
Run tests / test_sampleproject (3.13) (push) Waiting to run
This commit is contained in:
parent
c37628dea0
commit
28ff1d072a
11 changed files with 561 additions and 119 deletions
|
|
@ -65,25 +65,33 @@ and so `selected_items` will be set to `[1, 2, 3]`.
|
|||
|
||||
The defaults are aplied only to keyword arguments. They are NOT applied to positional arguments!
|
||||
|
||||
### Defaults from `Kwargs`
|
||||
|
||||
If you are using [`Component.Kwargs`](../fundamentals/typing_and_validation.md#typing-inputs) to specify the component input,
|
||||
you can set the defaults directly on `Kwargs`:
|
||||
|
||||
```python
|
||||
class ProfileCard(Component):
|
||||
class Kwargs:
|
||||
user_id: int
|
||||
show_details: bool = True
|
||||
```
|
||||
|
||||
Which is the same as:
|
||||
|
||||
```python
|
||||
class ProfileCard(Component):
|
||||
class Kwargs:
|
||||
user_id: int
|
||||
show_details: bool
|
||||
|
||||
class Defaults:
|
||||
show_details = True
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
||||
When [typing](../fundamentals/typing_and_validation.md) your components with [`Args`](../../../reference/api/#django_components.Component.Args),
|
||||
[`Kwargs`](../../../reference/api/#django_components.Component.Kwargs),
|
||||
or [`Slots`](../../../reference/api/#django_components.Component.Slots) classes,
|
||||
you may be inclined to define the defaults in the classes.
|
||||
|
||||
```py
|
||||
class ProfileCard(Component):
|
||||
class Kwargs:
|
||||
show_details: bool = True
|
||||
```
|
||||
|
||||
This is **NOT recommended**, because:
|
||||
|
||||
- The defaults will NOT be applied to inputs when using [`self.raw_kwargs`](../../../reference/api/#django_components.Component.raw_kwargs) property.
|
||||
- The defaults will NOT be applied when a field is given but set to `None`.
|
||||
|
||||
Instead, define the defaults in the [`Defaults`](../../../reference/api/#django_components.Component.Defaults) class.
|
||||
This works only when `Component.Kwargs` is a plain class, NamedTuple or dataclass.
|
||||
|
||||
### Default factories
|
||||
|
||||
|
|
@ -124,30 +132,28 @@ class MyTable(Component):
|
|||
|
||||
### Accessing defaults
|
||||
|
||||
Since the defaults are defined on the component class, you can access the defaults for a component with the [`Component.Defaults`](../../../reference/api#django_components.Component.Defaults) property.
|
||||
The defaults may be defined on both [`Component.Defaults`](../../../reference/api#django_components.Component.Defaults) and [`Component.Kwargs`](../../../reference/api#django_components.Component.Kwargs) classes.
|
||||
|
||||
So if we have a component like this:
|
||||
To get a final, merged dictionary of all the component's defaults, use [`get_component_defaults()`](../../../reference/api#django_components.get_component_defaults):
|
||||
|
||||
```py
|
||||
from django_components import Component, Default, register
|
||||
from django_components import Component, Default, get_component_defaults
|
||||
|
||||
@register("my_table")
|
||||
class MyTable(Component):
|
||||
class Kwargs:
|
||||
position: str
|
||||
order: int
|
||||
items: list[int]
|
||||
variable: str = "from_kwargs"
|
||||
|
||||
class Defaults:
|
||||
position = "left"
|
||||
selected_items = Default(lambda: [1, 2, 3])
|
||||
position: str = "left"
|
||||
items = Default(lambda: [1, 2, 3])
|
||||
|
||||
def get_template_data(self, args, kwargs, slots, context):
|
||||
return {
|
||||
"position": kwargs["position"],
|
||||
"selected_items": kwargs["selected_items"],
|
||||
}
|
||||
```
|
||||
|
||||
We can access individual defaults like this:
|
||||
|
||||
```py
|
||||
print(MyTable.Defaults.position)
|
||||
print(MyTable.Defaults.selected_items)
|
||||
defaults = get_component_defaults(MyTable)
|
||||
# {
|
||||
# "position": "left",
|
||||
# "items": [1, 2, 3],
|
||||
# "variable": "from_kwargs",
|
||||
# }
|
||||
```
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@ Each method handles the data independently - you can define different data for t
|
|||
class ProfileCard(Component):
|
||||
class Kwargs:
|
||||
user_id: int
|
||||
show_details: bool
|
||||
|
||||
class Defaults:
|
||||
show_details = True
|
||||
show_details: bool = True
|
||||
|
||||
def get_template_data(self, args, kwargs: Kwargs, slots, context):
|
||||
user = User.objects.get(id=kwargs.user_id)
|
||||
|
|
@ -304,7 +301,7 @@ class ProfileCard(Component):
|
|||
|
||||
## Default values
|
||||
|
||||
You can use [`Defaults`](../../../reference/api/#django_components.Component.Defaults) class to provide default values for your inputs.
|
||||
You can use the [`Defaults`](../../../reference/api/#django_components.Component.Defaults) and [`Kwargs`](../../../reference/api/#django_components.Component.Kwargs) classes to provide default values for your inputs.
|
||||
|
||||
These defaults will be applied either when:
|
||||
|
||||
|
|
@ -321,12 +318,9 @@ from django_components import Component, Default, register
|
|||
@register("profile_card")
|
||||
class ProfileCard(Component):
|
||||
class Kwargs:
|
||||
show_details: bool
|
||||
# Will be set to True if `None` or missing
|
||||
show_details: bool = True
|
||||
|
||||
class Defaults:
|
||||
show_details = True
|
||||
|
||||
# show_details will be set to True if `None` or missing
|
||||
def get_template_data(self, args, kwargs: Kwargs, slots, context):
|
||||
return {
|
||||
"show_details": kwargs.show_details,
|
||||
|
|
@ -335,26 +329,6 @@ class ProfileCard(Component):
|
|||
...
|
||||
```
|
||||
|
||||
!!! warning
|
||||
|
||||
When typing your components with [`Args`](../../../reference/api/#django_components.Component.Args),
|
||||
[`Kwargs`](../../../reference/api/#django_components.Component.Kwargs),
|
||||
or [`Slots`](../../../reference/api/#django_components.Component.Slots) classes,
|
||||
you may be inclined to define the defaults in the classes.
|
||||
|
||||
```py
|
||||
class ProfileCard(Component):
|
||||
class Kwargs:
|
||||
show_details: bool = True
|
||||
```
|
||||
|
||||
This is **NOT recommended**, because:
|
||||
|
||||
- The defaults will NOT be applied to inputs when using [`self.raw_kwargs`](../../../reference/api/#django_components.Component.raw_kwargs) property.
|
||||
- The defaults will NOT be applied when a field is given but set to `None`.
|
||||
|
||||
Instead, define the defaults in the [`Defaults`](../../../reference/api/#django_components.Component.Defaults) class.
|
||||
|
||||
## Accessing Render API
|
||||
|
||||
All three data methods have access to the Component's [Render API](../render_api), which includes:
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ or [`get_css_data()`](../../reference/api#django_components.Component.get_css_da
|
|||
To make things easier, Components can specify their defaults. Defaults are used when
|
||||
no value is provided, or when the value is set to `None` for a particular input.
|
||||
|
||||
To define defaults for a component, you create a nested `Defaults` class within your
|
||||
To define defaults for a component, you create a nested [`Defaults`](../../reference/api#django_components.Component.Defaults) class within your
|
||||
[`Component`](../../reference/api#django_components.Component) class.
|
||||
Each attribute in the `Defaults` class represents a default value for a corresponding input.
|
||||
|
||||
|
|
@ -255,6 +255,57 @@ class Calendar(Component):
|
|||
}
|
||||
```
|
||||
|
||||
### 6. Add input validation
|
||||
|
||||
Right now our `Calendar` component accepts any number of args and kwargs,
|
||||
and we can't see which ones are being used.
|
||||
|
||||
*This is a maintenance nightmare!*
|
||||
|
||||
Let's be good colleagues and document the component inputs.
|
||||
As a bonus, we will also get runtime validation of these inputs.
|
||||
|
||||
For defining component inputs, there's 3 options:
|
||||
|
||||
- [`Args`](../../reference/api#django_components.Component.Args) - For defining positional args passed to the component
|
||||
- [`Kwargs`](../../reference/api#django_components.Component.Kwargs) - For keyword args
|
||||
- [`Slots`](../../reference/api#django_components.Component.Slots) - For slots
|
||||
|
||||
Our calendar component is using only kwargs, so we can ignore `Args` and `Slots`.
|
||||
The new `Kwargs` class defines fields that this component accepts:
|
||||
|
||||
```py
|
||||
from django_components import Component, Default, register
|
||||
|
||||
@register("calendar")
|
||||
class Calendar(Component):
|
||||
template_file = "calendar.html"
|
||||
|
||||
class Kwargs: # <--- changed (replaced Defaults)
|
||||
date: Date
|
||||
extra_class: str = "text-blue"
|
||||
|
||||
def get_template_data(self, args, kwargs: Kwargs, slots, context): # <--- changed
|
||||
workweek_date = to_workweek_date(kwargs.date) # <--- changed
|
||||
return {
|
||||
"date": workweek_date,
|
||||
"extra_class": kwargs.extra_class, # <--- changed
|
||||
}
|
||||
```
|
||||
|
||||
Notice that:
|
||||
|
||||
- When we defined `Kwargs` class, the `kwargs` parameter to `get_template_data`
|
||||
changed to an instance of `Kwargs`. Fields are now accessed as attributes.
|
||||
- Since `kwargs` is of class `Kwargs`, we've added annotation to the `kwargs` parameter.
|
||||
- `Kwargs` replaced `Defaults`, because defaults can be defined also on `Kwargs` class.
|
||||
|
||||
And that's it! Now you can sleep safe knowing you won't break anything when
|
||||
adding or removing component inputs.
|
||||
|
||||
Read more about [Component defaults](../concepts/fundamentals/component_defaults.md)
|
||||
and [Typing and validation](../concepts/fundamentals/typing_and_validation.md).
|
||||
|
||||
---
|
||||
|
||||
Next, you will learn [how to use slots give your components even more flexibility ➡️](./adding_slots.md)
|
||||
|
|
|
|||
|
|
@ -171,6 +171,10 @@
|
|||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.get_component_defaults
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
||||
::: django_components.get_component_dirs
|
||||
options:
|
||||
show_if_no_docstring: true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue