从 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 字符串)

如何在 FastAPI 中使用 Pydantic 模型和表单数据?

如何从Python中列表的dict中的值生成所有组合

如何使用列表中的值作为 pydantic 验证器?