A commonly encountered issue in Dash is the inability to return custom types in callbacks. E.g., if you create a callback that returns a custom type like this,
...
@dataclass
class Person:
name: str
date_of_birth: datetime
...
@app.callback(...)
def submit(_, name, date):
dt = datetime.fromisoformat(date)
return Person(name, dt)
You’ll get a serialization error,
dash.exceptions.InvalidCallbackReturnValue: The callback for `<Output `store.data`>`
returned a value having type `Person`
which is not JSON serializable.
inspired by a PR from gedemagt, the dash-extensions==1.0.3
release includes a new DataclassTransform
which enables serialization of dataclasses automagically. The only modification needed is a type hint for the callback reading the data,
@app.callback(...)
def log(person: Person):
return f"{person.name} was born on {person.date_of_birth}" # no (manual) deserialization here
For reference, here is a full example,
from datetime import datetime
from dataclasses import dataclass
from dash_extensions.enrich import DashProxy, Input, html, DataclassTransform, dcc, Output, State
@dataclass
class Person:
name: str
date_of_birth: datetime
app = DashProxy(transforms=[DataclassTransform()])
app.layout = html.Div([
dcc.Input(id="name", value="John Doe"),
dcc.DatePickerSingle(id="picker", date=datetime(1990, 1, 1)),
dcc.Store(id="store"),
html.Button("Submit", id="btn"),
html.Div(id="log")
])
@app.callback(
Input("btn", "n_clicks"),
State("name", "value"),
State("picker", "date"),
Output("store", "data")
)
def submit(_, name, date):
dt = datetime.fromisoformat(date)
return Person(name, dt) # no (manual) serialization here
@app.callback(
Output("log", "children"),
Input("store", "data"),
)
def log(person: Person):
return f"{person.name} was born on {person.date_of_birth}"
if __name__ == "__main__":
app.run_server()