从 dict 生成 pydantic 模型
Posted
技术标签:
【中文标题】从 dict 生成 pydantic 模型【英文标题】:Generate pydantic model from a dict 【发布时间】:2020-09-27 17:48:02 【问题描述】:是否有一种直接的方法可以从字典中生成 Pydantic 模型?
这是我拥有的数据示例。
'id': '424c015f-7170-4ac5-8f59-096b83fe5f5806082020',
'contacts': [
'displayName': 'Norma Fisher',
'id': '544aa395-0e63-4f9a-8cd4-767b3040146d'
],
'startTime': '2020-06-08T09:38:00+00:00'
期待一个类似于...的模型
class NewModel(BaseModel):
id: str
contacts: list
startTime: str
【问题讨论】:
【参考方案1】:没有确切的方法,但如果您知道字段类型,则可以使用create_model()
创建模型。
或者有datamodel-code-generator(单独的包),它允许您从模式定义生成模型。
【讨论】:
datamodel-codegen --input file.json --output model.py --input-file-type json【参考方案2】:您可以使用MyModel.parse_obj(my_dict)
从字典中生成模型。根据documentation——
这与模型的
__init__
方法非常相似,不同之处在于它采用字典而不是关键字参数。
【讨论】:
【参考方案3】:我使用此方法在运行时使用字典定义生成模型。这种方法也允许您定义嵌套模型。字段类型语法借鉴了 create_model 方法。
from pydantic import create_model
m =
"a":(int,...),
"b":
"c":(str,"hi"),
"d":
"e":(bool,True),
"f":(float,0.5)
def dict_model(name:str,dict_def:dict):
fields =
for field_name,value in dict_def.items():
if isinstance(value,tuple):
fields[field_name]=value
elif isinstance(value,dict):
fields[field_name]=(dict_model(f'name_field_name',value),...)
else:
raise ValueError(f"Field field_name:value has invalid syntax")
return create_model(name,**fields)
model = dict_model("some_name",m)
【讨论】:
【参考方案4】:虽然我喜欢@data_wiz 字典定义,但这里有一个替代建议,它基于我需要即时获取简单的 JSON 响应,这些响应通常是 CamelCase 关键元素,并能够将其处理成 Python 风格的类。
但是,使用标准函数 JSON 可以轻松转换为 Dict! 我想以 pythonic 风格来解决这个问题 我还希望能够有一些类型覆盖将字符串转换为 pythonic 类型 我还想指出可选的元素。这就是我开始喜欢 Pydantic 的地方。
以下代码 sn-p 可以从 JSON API 响应的实际数据 Dict 生成模型,因为键是驼峰式,它会将它们转换为蟒蛇风格,但保留驼峰式作为别名。
这种 pydantic 别名可以轻松使用转换为 Dict 的 JSON,而无需进行密钥转换,也可以直接导出 JSON 格式的输出。注意观察动态模型 DynamicModel.__config__.allow_population_by_field_name = True
的配置,这允许从别名或 Pythonic 字段名称创建动态模型。
此代码功能不全,目前无法处理列表,但它对我来说适用于简单的情况。 使用示例在 pydanticModelGenerator 的文档字符串中
from inflection import underscore
from typing import Any, Dict, Optional
from pydantic import BaseModel, Field, create_model
class ModelDef(BaseModel):
"""Assistance Class for Pydantic Dynamic Model Generation"""
field: str
field_alias: str
field_type: Any
class pydanticModelGenerator:
"""
Takes source_data:Dict ( a single instance example of something like a JSON node) and self generates a pythonic data model with Alias to original source field names. This makes it easy to popuate or export to other systems yet handle the data in a pythonic way.
Being a pydantic datamodel all the richness of pydantic data validation is available and these models can easily be used in FastAPI and or a ORM
It does not process full JSON data structures but takes simple JSON document with basic elements
Provide a model_name, an example of JSON data and a dict of type overrides
Example:
source_data = 'Name': '48 Rainbow Rd',
'GroupAddressStyle': 'ThreeLevel',
'LastModified': '2020-12-21T07:02:51.2400232Z',
'ProjectStart': '2020-12-03T07:36:03.324856Z',
'Comment': '',
'CompletionStatus': 'Editing',
'LastUsedPuid': '955',
'Guid': '0c85957b-c2ae-4985-9752-b300ab385b36'
source_overrides = 'Guid':'type':uuid.UUID,
'LastModified':'type':datetime ,
'ProjectStart':'type':datetime ,
source_optionals = "Comment":True
#create Model
model_Project=pydanticModelGenerator(
model_name="Project",
source_data=source_data,
overrides=source_overrides,
optionals=source_optionals).generate_model()
#create instance using DynamicModel
project_instance=model_Project(**project_info)
"""
def __init__(
self,
model_name: str = None,
source_data: str = None,
overrides: Dict = ,
optionals: Dict = ,
):
def field_type_generator(k, overrides, optionals):
pass
field_type = str if not overrides.get(k) else overrides[k]["type"]
return field_type if not optionals.get(k) else Optional[field_type]
self._model_name = model_name
self._json_data = source_data
self._model_def = [
ModelDef(
field=underscore(k),
field_alias=k,
field_type=field_type_generator(k, overrides, optionals),
)
for k in source_data.keys()
]
def generate_model(self):
"""
Creates a pydantic BaseModel
from the json and overrides provided at initialization
"""
fields =
d.field: (d.field_type, Field(alias=d.field_alias)) for d in self._model_def
DynamicModel = create_model(self._model_name, **fields)
DynamicModel.__config__.allow_population_by_field_name = True
return DynamicModel
【讨论】:
【参考方案5】:另外,可以使用__init方法,
your_mode = YourMode(**your_dict)
【讨论】:
以上是关于从 dict 生成 pydantic 模型的主要内容,如果未能解决你的问题,请参考以下文章
pydantic学习与使用-7.字段顺序field-ordering
从冗长的字典生成时,Pandas DataFrame.from_dict() 性能不佳
pydantic 转换为 jsonable dict(不是完整的 json 字符串)