Skip to content

Dydantic

GitHub Repo PyPI

Dydantic is a Python library for dynamically generating Pydantic models from JSON Schema. It provides a convenient way to create Pydantic models on-the-fly from general user-defined schemas.

dyno

Install

pip install -U dydantic

Reference

def create_model_from_schema(json_schema: Dict[str, Any],*, **kwargs: Any) -> Type[BaseModel]:

Create a Pydantic model from a JSON schema.

This function takes a JSON schema as input and dynamically creates a Pydantic model class based on the schema. It supports various JSON schema features such as nested objects, referenced definitions, custom configurations, custom base classes, custom validators, and more.

Parameters:

  • json_schema (Dict[str, Any]) –

    A dictionary representing the JSON schema.

  • root_schema (Optional[Dict[str, Any]], default: None ) –

    The root schema that contains the current schema. If not provided, the current schema will be treated as the root schema.

  • __config__ (ConfigDict | None, default: None ) –

    Pydantic configuration for the generated model.

  • __base__ (None, default: None ) –

    Base class for the generated model. Defaults to pydantic.BaseModel.

  • __module__ (str, default: __name__ ) –

    Module name for the generated model class. Defaults to current module.

  • __validators__ (dict[str, AnyClassMethod] | None, default: None ) –

    A dictionary of custom validators for the generated model.

  • __cls_kwargs__ (dict[str, Any] | None, default: None ) –

    Additional keyword arguments for the generated model class.

Returns:

  • Type[BaseModel]

    A dynamically created Pydantic model class based on the provided JSON schema.

Examples:

  1. Simple schema with string and integer properties:

    >>> json_schema = {
    ...     "title": "Person",
    ...     "type": "object",
    ...     "properties": {
    ...         "name": {"type": "string"},
    ...         "age": {"type": "integer"},
    ...     },
    ...     "required": ["name"],
    ... }
    >>> Person = create_model_from_schema(json_schema)
    >>> person = Person(name="John", age=30)
    >>> person
    Person(name='John', age=30)
    
  2. Schema with a nested object:

    >>> json_schema = {
    ...     "title": "Employee",
    ...     "type": "object",
    ...     "properties": {
    ...         "name": {"type": "string"},
    ...         "age": {"type": "integer"},
    ...         "address": {
    ...             "type": "object",
    ...             "properties": {
    ...                 "street": {"type": "string"},
    ...                 "city": {"type": "string"},
    ...             },
    ...         },
    ...     },
    ...     "required": ["name", "address"],
    ... }
    >>> Employee = create_model_from_schema(json_schema)
    >>> employee = Employee(
    ...     name="Alice", age=25, address={"street": "123 Main St", "city": "New York"}
    ... )
    >>> employee
    Employee(name='Alice', age=25, address=Address(street='123 Main St', city='New York'))
    
  3. Schema with a referenced definition:

    >>> json_schema = {
    ...     "title": "Student",
    ...     "type": "object",
    ...     "properties": {
    ...         "name": {"type": "string"},
    ...         "grade": {"type": "integer"},
    ...         "school": {"$ref": "#/$defs/School"},
    ...     },
    ...     "required": ["name", "school"],
    ...     "$defs": {
    ...         "School": {
    ...             "type": "object",
    ...             "properties": {
    ...                 "name": {"type": "string"},
    ...                 "address": {"type": "string"},
    ...             },
    ...             "required": ["name"],
    ...         },
    ...     },
    ... }
    >>> Student = create_model_from_schema(json_schema)
    >>> student = Student(
    ...     name="Bob", grade=10, school={"name": "ABC School", "address": "456 Elm St"}
    ... )
    >>> student
    Student(name='Bob', grade=10, school=School(name='ABC School', address='456 Elm St'))
    
  4. Schema with a custom configuration:

    >>> json_schema = {
    ...     "title": "User",
    ...     "type": "object",
    ...     "properties": {
    ...         "username": {"type": "string"},
    ...         "email": {"type": "string", "format": "email"},
    ...     },
    ...     "required": ["username", "email"],
    ... }
    >>> config = ConfigDict(extra="forbid")
    >>> User = create_model_from_schema(json_schema, __config__=config)
    >>> user = User(username="john_doe", email="john@example.com")
    >>> user
    User(username='john_doe', email='john@example.com')
    
  5. Schema with a custom base class:

    >>> class CustomBaseModel(BaseModel):
    ...     class Config:
    ...         frozen = True
    >>> json_schema = {
    ...     "title": "Product",
    ...     "type": "object",
    ...     "properties": {
    ...         "name": {"type": "string"},
    ...         "price": {"type": "number"},
    ...     },
    ...     "required": ["name", "price"],
    ... }
    >>> Product = create_model_from_schema(json_schema, __base__=CustomBaseModel)
    >>> product = Product(name="Phone", price=499.99)
    >>> product
    Product(name='Phone', price=499.99)
    >>> product.price = 599.99  # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
       ...
    pydantic_core._pydantic_core.ValidationError: 1 validation error for Product
    price
        Instance is frozen [type=frozen_instance, input_value=599.99, input_type=float]
    
  6. Schema with a custom validator:

    >>> from pydantic import field_validator
    >>> def price_validator(value):
    ...     if value <= 0:
    ...         raise ValueError("Price must be positive")
    ...     return value
    >>> json_schema = {
    ...     "title": "Item",
    ...     "type": "object",
    ...     "properties": {
    ...         "name": {"type": "string"},
    ...         "price": {"type": "number"},
    ...     },
    ...     "required": ["name", "price"],
    ... }
    >>> Item = create_model_from_schema(
    ...     json_schema,
    ...     __validators__={
    ...         "my_price_validator": field_validator("price")(price_validator)
    ...     },
    ... )
    >>> item = Item(name="Pen", price=-10)  # doctest: +IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ...
    pydantic_core._pydantic_core.ValidationError: 1 validation error for Item
    price
       ValueError: Price must be positive
    
  7. Schema with a union type using anyOf:

    >>> json_schema = {
    ...     "title": "SKU",
    ...     "type": "object",
    ...     "properties": {
    ...         "value": {
    ...             "title": "Value",
    ...             "anyOf": [
    ...                 {"type": "string"},
    ...                 {"type": "integer"},
    ...                 {"type": "boolean"},
    ...             ],
    ...         },
    ...     },
    ... }
    >>> Sku = create_model_from_schema(json_schema)
    >>> Sku(value="hello")
    SKU(value='hello')
    >>> Sku(value=42)
    SKU(value=42)
    >>> Sku(value=True)
    SKU(value=True)
    
  8. Schema with a string format:

    >>> json_schema = {
    ...     "title": "User",
    ...     "type": "object",
    ...     "properties": {
    ...         "username": {"type": "string"},
    ...         "email": {"type": "string", "format": "email"},
    ...         "password": {"type": "string", "format": "password"},
    ...     },
    ...     "required": ["username", "email", "password"],
    ... }
    >>> User = create_model_from_schema(json_schema)
    >>> user = User(username="john_doe", email="john@example.com", password="secret")
    >>> user
    User(username='john_doe', email='john@example.com', password=SecretStr('**********'))
    >>> user.password
    SecretStr('**********')
    
  9. Schema with an array of items:

    >>> json_schema = {
    ...     "title": "Numbers",
    ...     "type": "object",
    ...     "properties": {
    ...         "value": {
    ...             "type": "array",
    ...             "items": {"type": "integer"},
    ...         }
    ...     },
    ... }
    >>> Numbers = create_model_from_schema(json_schema)
    >>> numbers = Numbers(value=[1, 2, 3, 4, 5])
    >>> numbers
    Numbers(value=[1, 2, 3, 4, 5])
    
  10. Schema with a nested array of objects:

    >>> json_schema = {
    ...     "title": "Matrix",
    ...     "type": "object",
    ...     "properties": {
    ...         "value": {
    ...             "type": "array",
    ...             "items": {
    ...                 "type": "array",
    ...                 "items": {"type": "integer"},
    ...             },
    ...         },
    ...     },
    ... }
    >>> Matrix = create_model_from_schema(json_schema)
    >>> matrix = Matrix(value=[[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    >>> matrix
    Matrix(value=[[1, 2, 3], [4, 5, 6], [7, 8, 9]])
    
Source code in dydantic/_utils.py
def create_model_from_schema(
    json_schema: Dict[str, Any],
    *,
    root_schema: Optional[Dict[str, Any]] = None,
    __config__: ConfigDict | None = None,
    __base__: None = None,
    __module__: str = __name__,
    __validators__: dict[str, AnyClassMethod] | None = None,
    __cls_kwargs__: dict[str, Any] | None = None,
) -> Type[BaseModel]:
    """Create a Pydantic model from a JSON schema.

    This function takes a JSON schema as input and dynamically creates a Pydantic model class
    based on the schema. It supports various JSON schema features such as nested objects,
    referenced definitions, custom configurations, custom base classes, custom validators, and more.

    Args:
        json_schema: A dictionary representing the JSON schema.
        root_schema: The root schema that contains the current schema. If not provided, the
            current schema will be treated as the root schema.
        __config__: Pydantic configuration for the generated model.
        __base__: Base class for the generated model. Defaults to `pydantic.BaseModel`.
        __module__: Module name for the generated model class. Defaults to current module.
        __validators__: A dictionary of custom validators for the generated model.
        __cls_kwargs__: Additional keyword arguments for the generated model class.

    Returns:
        A dynamically created Pydantic model class based on the provided JSON schema.

    **Examples:**

    1. Simple schema with string and integer properties:

            >>> json_schema = {
            ...     "title": "Person",
            ...     "type": "object",
            ...     "properties": {
            ...         "name": {"type": "string"},
            ...         "age": {"type": "integer"},
            ...     },
            ...     "required": ["name"],
            ... }
            >>> Person = create_model_from_schema(json_schema)
            >>> person = Person(name="John", age=30)
            >>> person
            Person(name='John', age=30)

    2. Schema with a nested object:

            >>> json_schema = {
            ...     "title": "Employee",
            ...     "type": "object",
            ...     "properties": {
            ...         "name": {"type": "string"},
            ...         "age": {"type": "integer"},
            ...         "address": {
            ...             "type": "object",
            ...             "properties": {
            ...                 "street": {"type": "string"},
            ...                 "city": {"type": "string"},
            ...             },
            ...         },
            ...     },
            ...     "required": ["name", "address"],
            ... }
            >>> Employee = create_model_from_schema(json_schema)
            >>> employee = Employee(
            ...     name="Alice", age=25, address={"street": "123 Main St", "city": "New York"}
            ... )
            >>> employee
            Employee(name='Alice', age=25, address=Address(street='123 Main St', city='New York'))

    3. Schema with a referenced definition:

            >>> json_schema = {
            ...     "title": "Student",
            ...     "type": "object",
            ...     "properties": {
            ...         "name": {"type": "string"},
            ...         "grade": {"type": "integer"},
            ...         "school": {"$ref": "#/$defs/School"},
            ...     },
            ...     "required": ["name", "school"],
            ...     "$defs": {
            ...         "School": {
            ...             "type": "object",
            ...             "properties": {
            ...                 "name": {"type": "string"},
            ...                 "address": {"type": "string"},
            ...             },
            ...             "required": ["name"],
            ...         },
            ...     },
            ... }
            >>> Student = create_model_from_schema(json_schema)
            >>> student = Student(
            ...     name="Bob", grade=10, school={"name": "ABC School", "address": "456 Elm St"}
            ... )
            >>> student
            Student(name='Bob', grade=10, school=School(name='ABC School', address='456 Elm St'))

    4. Schema with a custom configuration:

            >>> json_schema = {
            ...     "title": "User",
            ...     "type": "object",
            ...     "properties": {
            ...         "username": {"type": "string"},
            ...         "email": {"type": "string", "format": "email"},
            ...     },
            ...     "required": ["username", "email"],
            ... }
            >>> config = ConfigDict(extra="forbid")
            >>> User = create_model_from_schema(json_schema, __config__=config)
            >>> user = User(username="john_doe", email="john@example.com")
            >>> user
            User(username='john_doe', email='john@example.com')

    5. Schema with a custom base class:

            >>> class CustomBaseModel(BaseModel):
            ...     class Config:
            ...         frozen = True
            >>> json_schema = {
            ...     "title": "Product",
            ...     "type": "object",
            ...     "properties": {
            ...         "name": {"type": "string"},
            ...         "price": {"type": "number"},
            ...     },
            ...     "required": ["name", "price"],
            ... }
            >>> Product = create_model_from_schema(json_schema, __base__=CustomBaseModel)
            >>> product = Product(name="Phone", price=499.99)
            >>> product
            Product(name='Phone', price=499.99)
            >>> product.price = 599.99  # doctest: +IGNORE_EXCEPTION_DETAIL
            Traceback (most recent call last):
               ...
            pydantic_core._pydantic_core.ValidationError: 1 validation error for Product
            price
                Instance is frozen [type=frozen_instance, input_value=599.99, input_type=float]

    6. Schema with a custom validator:

            >>> from pydantic import field_validator
            >>> def price_validator(value):
            ...     if value <= 0:
            ...         raise ValueError("Price must be positive")
            ...     return value
            >>> json_schema = {
            ...     "title": "Item",
            ...     "type": "object",
            ...     "properties": {
            ...         "name": {"type": "string"},
            ...         "price": {"type": "number"},
            ...     },
            ...     "required": ["name", "price"],
            ... }
            >>> Item = create_model_from_schema(
            ...     json_schema,
            ...     __validators__={
            ...         "my_price_validator": field_validator("price")(price_validator)
            ...     },
            ... )
            >>> item = Item(name="Pen", price=-10)  # doctest: +IGNORE_EXCEPTION_DETAIL
            Traceback (most recent call last):
            ...
            pydantic_core._pydantic_core.ValidationError: 1 validation error for Item
            price
               ValueError: Price must be positive

    7. Schema with a union type using `anyOf`:

            >>> json_schema = {
            ...     "title": "SKU",
            ...     "type": "object",
            ...     "properties": {
            ...         "value": {
            ...             "title": "Value",
            ...             "anyOf": [
            ...                 {"type": "string"},
            ...                 {"type": "integer"},
            ...                 {"type": "boolean"},
            ...             ],
            ...         },
            ...     },
            ... }
            >>> Sku = create_model_from_schema(json_schema)
            >>> Sku(value="hello")
            SKU(value='hello')
            >>> Sku(value=42)
            SKU(value=42)
            >>> Sku(value=True)
            SKU(value=True)

    8. Schema with a string format:

            >>> json_schema = {
            ...     "title": "User",
            ...     "type": "object",
            ...     "properties": {
            ...         "username": {"type": "string"},
            ...         "email": {"type": "string", "format": "email"},
            ...         "password": {"type": "string", "format": "password"},
            ...     },
            ...     "required": ["username", "email", "password"],
            ... }
            >>> User = create_model_from_schema(json_schema)
            >>> user = User(username="john_doe", email="john@example.com", password="secret")
            >>> user
            User(username='john_doe', email='john@example.com', password=SecretStr('**********'))
            >>> user.password
            SecretStr('**********')

    9. Schema with an array of items:

            >>> json_schema = {
            ...     "title": "Numbers",
            ...     "type": "object",
            ...     "properties": {
            ...         "value": {
            ...             "type": "array",
            ...             "items": {"type": "integer"},
            ...         }
            ...     },
            ... }
            >>> Numbers = create_model_from_schema(json_schema)
            >>> numbers = Numbers(value=[1, 2, 3, 4, 5])
            >>> numbers
            Numbers(value=[1, 2, 3, 4, 5])

    10. Schema with a nested array of objects:

            >>> json_schema = {
            ...     "title": "Matrix",
            ...     "type": "object",
            ...     "properties": {
            ...         "value": {
            ...             "type": "array",
            ...             "items": {
            ...                 "type": "array",
            ...                 "items": {"type": "integer"},
            ...             },
            ...         },
            ...     },
            ... }
            >>> Matrix = create_model_from_schema(json_schema)
            >>> matrix = Matrix(value=[[1, 2, 3], [4, 5, 6], [7, 8, 9]])
            >>> matrix
            Matrix(value=[[1, 2, 3], [4, 5, 6], [7, 8, 9]])

    """  # noqa: E501
    model_name = json_schema.get("title", "DynamicModel")
    field_definitions = {
        name: _json_schema_to_pydantic_field(
            name, prop, json_schema.get("required", []), root_schema or json_schema
        )
        for name, prop in (json_schema.get("properties", {}) or {}).items()
    }

    return create_model_base(
        model_name,
        __config__=__config__,
        __base__=__base__,
        __module__=__module__,
        __validators__=__validators__,
        __cls_kwargs__=__cls_kwargs__,
        **field_definitions,
    )

Contributing

Contributions to dydantic are welcome! If you find any issues or have suggestions for improvements, please open an issue or submit a pull request on the GitHub repository: https://github.com/hinthornw/dydantic

License

dydantic is open-source software licensed under the MIT License.

Built with ❤️ by Twitter