Skip to content

Code Generation with datamodel-code-generator

The datamodel-code-generator project is a library and command-line utility to generate pydantic models from just about any data source, including:

  • OpenAPI 3 (YAML/JSON)
  • JSON Schema
  • JSON/YAML/CSV Data (which will be converted to JSON Schema)
  • Python dictionary (which will be converted to JSON Schema)
  • GraphQL schema

Whenever you find yourself with any data convertible JSON but without pydantic models, this tool will allow you to generate type-safe model hierarchies on demand.

Installation

Terminal
pip install datamodel-code-generator

Example

In this case, datamodel-code-generator creates pydantic models from a JSON Schema file.

Terminal
datamodel-codegen  --input person.json --input-file-type jsonschema --output model.py

person.json:

{
  "$id": "person.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Person",
  "type": "object",
  "properties": {
    "first_name": {
      "type": "string",
      "description": "The person's first name."
    },
    "last_name": {
      "type": "string",
      "description": "The person's last name."
    },
    "age": {
      "description": "Age in years.",
      "type": "integer",
      "minimum": 0
    },
    "pets": {
      "type": "array",
      "items": [
        {
          "$ref": "#/definitions/Pet"
        }
      ]
    },
    "comment": {
      "type": "null"
    }
  },
  "required": [
      "first_name",
      "last_name"
  ],
  "definitions": {
    "Pet": {
      "properties": {
        "name": {
          "type": "string"
        },
        "age": {
          "type": "integer"
        }
      }
    }
  }
}

model.py:

# generated by datamodel-codegen:
#   filename:  person.json
#   timestamp: 2020-05-19T15:07:31+00:00
from __future__ import annotations

from typing import Any

from pydantic import BaseModel, Field, conint


class Pet(BaseModel):
    name: str | None = None
    age: int | None = None


class Person(BaseModel):
    first_name: str = Field(description="The person's first name.")
    last_name: str = Field(description="The person's last name.")
    age: conint(ge=0) | None = Field(None, description='Age in years.')
    pets: list[Pet] | None = None
    comment: Any | None = None

More information can be found on the official documentation.

Catching drift from the source schema

A model generated from an OpenAPI or JSON Schema document is a snapshot of that contract. When the upstream data stops matching it (a field changes type, a new required field appears), the mismatch surfaces as a ValidationError at runtime, often the first sign the source has drifted from the schema you generated against.

If you record validations with Logfire, those failures are stored with the payload that caused them, so you can see what changed and when it started, useful when you don’t own the schema and can’t regenerate the models until you know what moved.