【问题标题】:Generate pydantic model from a dict从 dict 生成 pydantic 模型
【发布时间】: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

【问题讨论】:

    标签: python pydantic


    【解决方案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)
          

          【讨论】:

            猜你喜欢
            • 2021-11-03
            • 1970-01-01
            • 2021-12-28
            • 1970-01-01
            • 2023-04-10
            • 1970-01-01
            • 2022-10-04
            • 1970-01-01
            • 2021-01-07
            相关资源
            最近更新 更多