Pydantic-backed JSON editor¶
Source: examples/panels/jsoneditor/jsoneditor_pydantic.py
Test: tests/panels/jsoneditor/examples/test_jsoneditor_pydantic.py
Demonstrates a PydanticEditor subclass of JsonEditor that derives its schema from a Pydantic model and accepts model instances as the initial value.
The code¶
from panelini.panels.jsoneditor import JsonEditor
from pydantic import BaseModel, Field
from pydantic._internal._model_construction import ModelMetaclass
def _apply_formats(schema, array_tabs, dict_categories):
"""Recursively inject 'tabs' / 'categories' format hints into a JSON schema."""
...
class PydanticEditor(JsonEditor):
def __init__(
self,
pydantic_model: ModelMetaclass,
value=None,
format_array_tabs: bool = False,
format_dict_categories: bool = False,
**params,
):
json_schema = pydantic_model.model_json_schema()
if format_array_tabs or format_dict_categories:
json_schema = _apply_formats(json_schema, format_array_tabs, format_dict_categories)
params.setdefault("options", {})["schema"] = json_schema
super().__init__(**params)
if isinstance(value, BaseModel):
value = value.model_dump()
self.value = value
Key points:
Schema derivation —
model_json_schema()converts the Pydantic model (including nested models and$defs) into the JSON Schema consumed byjson-editor.Pydantic instance as value — if
valueis aBaseModel, it is converted withmodel_dump()before being assigned to thevalueparam, so callers never need to serialize manually.Initial value preserved on serve —
valueis passed asstartvalto the JavaScript editor at mount time, preventing the firstchangeevent from resetting the form to schema defaults.format_array_tabs— adds"format": "tabs"to every"type": "array"node in the schema, rendering list items as tabs.format_dict_categories— adds"format": "categories"to every"type": "object"node, rendering object properties as category panels.
Data flow¶
graph LR
model([Pydantic model]) -- "model_json_schema()" --> schema([JSON Schema])
schema --> je[PydanticEditor]
instance([Pydantic instance]) -- "model_dump()" --> val([dict value])
val -- "startval" --> je
je -- "value sync" --> py[Python]
py -- "set_value" --> je
How the test exercises it¶
The Playwright test:
Defines the same Pydantic models as the example (
A,ASub) locally so the example file stays self-contained.Creates a fresh
PydanticEditorvia a pytest fixture.Serves it on a random port and navigates a headless browser to the URL.
Asserts that
my_editor.valuestill equalsa.model_dump()after JS initialisation (regression test for the initial-value reset bug).Fills the
xfield with42, blurs the input, and assertsmy_editor.value["x"] == 42(round-trip test).
See also¶
JsonEditor — full
JsonEditorguide including initial value and Pydantic integration sections