Skip to content

Typing and validation

Adding type hints with Generics¤

New in version 0.92

The Component class optionally accepts type parameters that allow you to specify the types of args, kwargs, slots, and data.

Use this to add type hints to your components, or to validate component inputs.

from django_components import Component

ButtonType = Component[Args, Kwargs, Slots, Data, JsData, CssData]

class Button(ButtonType):
    template_file = "button.html"

    def get_context_data(self, *args, **kwargs):
        ...

The generic parameters are:

  • Args - Positional arguments, must be a Tuple or Any
  • Kwargs - Keyword arguments, must be a TypedDict or Any
  • Slots - Slots, must be a TypedDict or Any
  • Data - Data returned from get_context_data(), must be a TypedDict or Any
  • JsData - Data returned from get_js_data(), must be a TypedDict or Any
  • CssData - Data returned from get_css_data(), must be a TypedDict or Any

Example¤

from typing import NotRequired, Tuple, TypedDict
from pydantic import BaseModel
from django_components import Component, SlotContent, SlotFunc

###########################################
# 1. Define the types
###########################################

# Positional inputs
ButtonArgs = Tuple[str, ...]

# Keyword inputs
class ButtonKwargs(TypedDict):
    name: str
    age: int
    maybe_var: NotRequired[int] # May be ommited

# The data available to the `footer` scoped slot
class ButtonFooterSlotData(TypedDict):
    value: int

# Slots
class ButtonSlots(TypedDict):
    # SlotContent == str or slot func
    header: SlotContent
    # Use SlotFunc for slot functions.
    # The generic specifies the data available to the slot function
    footer: NotRequired[SlotFunc[ButtonFooterSlotData]]

# Data returned from `get_context_data`
class ButtonData(BaseModel):
    data1: str
    data2: int

# Data returned from `get_js_data`
class ButtonJsData(BaseModel):
    js_data1: str
    js_data2: int

# Data returned from `get_css_data`
class ButtonCssData(BaseModel):
    css_data1: str
    css_data2: int

###########################################
# 2. Define the component with those types
###########################################

ButtonType = Component[
    ButtonArgs,
    ButtonKwargs,
    ButtonSlots,
    ButtonData,
    ButtonJsData,
    ButtonCssData,
]

class Button(ButtonType):
    def get_context_data(self, *args, **kwargs):
        ...

When you then call Component.render or Component.render_to_response, you will get type hints:

Button.render(
    # ERROR: Expects a string
    args=(123,),
    kwargs={
        "name": "John",
        # ERROR: Expects an integer
        "age": "invalid",
    },
    slots={
        "header": "...",
        # ERROR: Expects key "footer"
        "foo": "invalid",
    },
)

If you don't want to validate some parts, set them to Any.

ButtonType = Component[
    ButtonArgs,
    ButtonKwargs,
    ButtonSlots,
    Any,
    Any,
    Any,
]

class Button(ButtonType):
    ...

Passing variadic args and kwargs¤

You may have a function that accepts a variable number of args or kwargs:

def get_context_data(self, *args, **kwargs):
    ...

This is not supported with the typed components.

As a workaround:

  • For a variable number of positional arguments (*args), set a positional argument that accepts a list of values:

    # Tuple of one member of list of strings
    Args = Tuple[List[str]]
    
  • For a variable number of keyword arguments (**kwargs), set a keyword argument that accepts a dictionary of values:

    class Kwargs(TypedDict):
        variable: str
        another: int
        # Pass any extra keys under `extra`
        extra: Dict[str, any]
    

Handling no args or no kwargs¤

To declare that a component accepts no args, kwargs, etc, you can use the EmptyTuple and EmptyDict types:

from django_components import Component, EmptyDict, EmptyTuple

class Button(Component[EmptyTuple, EmptyDict, EmptyDict, EmptyDict, EmptyDict, EmptyDict]):
    ...

Runtime input validation with types¤

Warning

Input validation was part of Django Components from version 0.96 to 0.135.

Since v0.136, input validation is available as a separate extension.

To enable input validation, you need to install the djc-ext-pydantic extension:

pip install djc-ext-pydantic

And add the extension to your project:

COMPONENTS = {
    "extensions": [
        "djc_pydantic.PydanticExtension",
    ],
}

djc-ext-pydantic integrates Pydantic for input and data validation. It uses the types defined on the component's class to validate inputs of Django components.

Usage for Python <3.11¤

On Python 3.8-3.10, use typing_extensions

from typing_extensions import TypedDict, NotRequired

Additionally on Python 3.8-3.9, also import annotations:

from __future__ import annotations

Moreover, on 3.10 and less, you may not be able to use NotRequired, and instead you will need to mark either all keys are required, or all keys as optional, using TypeDict's total kwarg.

See PEP-655 for more info.