shelper
shelper

Reputation: 10583

How to create dynamic models using pydantic and a dict data type

I am trying to load a yml file into a dict, with pyyaml, theloading process automatically loads proper types for me, e.g., yml file with content below:

key1: test
key2: 100

will be loaded into a dict of {"key1": "test", "key2":100} where the type of key1's value is string, and the type of key2's value is int.

Now I want to dynamically create a class based on this dict, basically a class that has the dict keys as fields and dict values as values as shown below:

class Test:
    key1: str = "test"
    key2: int = 100

I believe I can do something like below using Pydantic:

Test = create_model('Test', key1=(str, "test"), key2=(int, 100))

However, as shown here, I have to manually tell create_model what keys and types for creating this model. I wonder if there is a away to automatically use the items in the dict to create model?

Upvotes: 3

Views: 4924

Answers (1)

alex_noname
alex_noname

Reputation: 32273

Yes, for example, you could call create_model recursively for dictionary items, passing keys and value types, the values themselves can become default values.

Example (not production ready):

from typing import Dict
from pydantic import create_model

test_dict = {'a': 12, 'nested': {'b': 'value', 'c': 1}}


def _make_model(v, name):
    if type(v) is dict:
        return create_model(name, **{k: _make_model(v, k) for k, v in v.items()}), ...
    return type(v), v


def make_model(v: Dict, name: str):
    return _make_model(v, name)[0]


model = make_model(test_dict, 'MyModel')
m = model.parse_obj(test_dict)
print(m)
print(model.schema_json(indent=2))

Output:

a=12 nested=nested(b='value', c=1)

{
  "title": "MyModel",
  "type": "object",
  "properties": {
    "a": {
      "title": "A",
      "default": 12,
      "type": "integer"
    },
    "nested": {
      "$ref": "#/definitions/nested"
    }
  },
  "required": [
    "nested"
  ],
  "definitions": {
    "nested": {
      "title": "nested",
      "type": "object",
      "properties": {
        "b": {
          "title": "B",
          "default": "value",
          "type": "string"
        },
        "c": {
          "title": "C",
          "default": 1,
          "type": "integer"
        }
      }
    }
  }
}

Upvotes: 4

Related Questions