Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate dynamic model using pydantic

I am trying to create a dynamic model using Python's pydantic library. My input data is a regular dict. However, the content of the dict (read: its keys) may vary.
I am wondering how to dynamically create a pydantic model which is dependent on the dict's content?

I created a toy example with two different dicts (inputs1 and inputs2). Let's assume the nested dict called strategy may be different. Based on strategy/name I know in advance which fields will exist in strategy. I need to create the pydantic model based on strategy/name.

from pydantic import BaseModel

inputs1 = {
    "universe": {"name": "test_universe", "ccy": "USD"},
    "price_src": "csv",
    "strategy": {"name": "test_strat1"},
}
inputs2 = {
    "universe": {"name": "test_universe", "ccy": "USD"},
    "price_src": "csv",
    "strategy": {"name": "test_strat2", "periods": 10},
}


class Universe(BaseModel):
    name: str
    ccy: str = "EUR"


strategy_name = "test_strat2"

if strategy_name == "test_strat1":
    inputs = inputs1

    class Strategy(BaseModel):
        name: str


elif strategy_name == "test_strat2":
    inputs = inputs2

    class Strategy(BaseModel):
        name: str
        periods: int


class StaticModel(BaseModel):
    universe: Universe
    price_src: str = "csv"
    strategy: Strategy


static_model = StaticModel(**inputs)

My expected output if ``strategy_name == "test_strat1":

universe=Universe(name='test_universe', ccy='USD') price_src='csv' strategy=Strategy(name='test_strat1')

My expected output if ``strategy_name == "test_strat2":

universe=Universe(name='test_universe', ccy='USD') price_src='csv' strategy=Strategy(name='test_strat2', periods=10)

I was thinking about using pydantic's create_model function. However, I don't understand how to dynamically define the fields.

like image 579
Andi Avatar asked Sep 10 '25 21:09

Andi


2 Answers

For the dynamic creation of pydantic models, you can use create_model. Like so:

from pydantic import create_model

d = {"strategy": {"name": "test_strat2", "periods": 10}}

Strategy = create_model("Strategy", **d["strategy"])

print(Strategy.schema_json(indent=2))

Output:

{
  "title": "Strategy",
  "type": "object",
  "properties": {
    "name": {
      "title": "Name",
      "default": "test_strat2",
      "type": "string"
    },
    "periods": {
      "title": "Periods",
      "default": 10,
      "type": "integer"
    }
  }
}

like image 135
alex_noname Avatar answered Sep 12 '25 12:09

alex_noname


You can use create_model with key=(type, ...) (3 dots) to declare a field without default value. For example:

from pydantic import BaseModel, create_model

...
if strategy_name == "test_strat1":
    inputs = inputs1
    Strategy = create_model('Strategy', name=(str, ...))

elif strategy_name == "test_strat2":
    inputs = inputs2
    Strategy = create_model('Strategy', name=(str, ...), periods=(int, ...))

print(Strategy.schema_json(indent=2))

Output for test_strat1:

{
  ...
  "properties": {
    "name": {
      "title": "Name",
      "type": "string"
    }
  },
  "required": [
    "name"
  ]
}

And for test_strat2:

{
  ...
  "properties": {
    "name": {
      "title": "Name",
      "type": "string"
    },
    "periods": {
      "title": "Periods",
      "type": "integer"
    }
  },
  "required": [
    "name",
    "periods"
  ]
}

Related Pydantic documentation: https://pydantic-docs.helpmanual.io/usage/models/#dynamic-model-creation

You can see in the docs that:

  • foo=(str, ...) - str-typed foo attribute with no defaults
  • bar=123 - int-typed bar attribute with a default value of 123.
like image 28
Khanh Luong Avatar answered Sep 12 '25 11:09

Khanh Luong