在Pydantic中直接获取根模型的列表及字典表示的方法
Great question! You don't have to stick with the somewhat clunky __root__ access—there are cleaner ways to get your validated list and dict representations directly. Here are a few tailored approaches for your use case:
1. Use parse_obj_as (No Extra Model Needed)
If you don't need a dedicated DeviceList model, Pydantic's parse_obj_as function lets you validate a list of objects directly, skipping the intermediate root model entirely:
from datetime import datetime from typing import List from pydantic import BaseModel, Field, parse_obj_as my_devices = { "devices": [ { "modifyTimestamp": "2022-01-17T13:35:58.597569Z", "vehicleId": "VINWVWZZZAUZLW9191234", } ], "modifyTimestamp": "2022-01-17T13:35:58.597569Z", } class Device(BaseModel): vehicle_id: str = Field(alias="vehicleId") modify_timestamp: datetime = Field(alias="modifyTimestamp") # Validate directly as a list of Device instances validated_data = parse_obj_as(List[Device], my_devices["devices"]) # Now validated_data is already a list—no __root__ required! validated_data_list = [v.dict() for v in validated_data] print(validated_data) print(validated_data_list)
This returns exactly what you need with minimal boilerplate.
2. Make DeviceList Iterable (For Pydantic V1 or Custom Models)
If you want to keep a dedicated DeviceList model, you can add sequence methods to it so you can iterate over the instance directly instead of always accessing __root__:
from datetime import datetime from typing import List from pydantic import BaseModel, Field from collections.abc import Sequence my_devices = { "devices": [ { "modifyTimestamp": "2022-01-17T13:35:58.597569Z", "vehicleId": "VINWVWZZZAUZLW9191234", } ], "modifyTimestamp": "2022-01-17T13:35:58.597569Z", } class Device(BaseModel): vehicle_id: str = Field(alias="vehicleId") modify_timestamp: datetime = Field(alias="modifyTimestamp") class DeviceList(BaseModel, Sequence): __root__: List[Device] # Delegate sequence operations to __root__ def __getitem__(self, index): return self.__root__[index] def __len__(self): return len(self.__root__) validated_data = DeviceList(__root__=my_devices["devices"]) # Now you can iterate directly over validated_data validated_data_list = [v.dict() for v in validated_data] print(validated_data) print(validated_data_list)
With this setup, you can treat validated_data just like a regular list for iteration, indexing, and length checks.
3. Use RootModel (Pydantic V2+)
If you're using Pydantic V2, the official recommended way to handle root-level lists is with RootModel, which is designed to simplify this exact scenario:
from datetime import datetime from typing import List from pydantic import BaseModel, Field, RootModel my_devices = { "devices": [ { "modifyTimestamp": "2022-01-17T13:35:58.597569Z", "vehicleId": "VINWVWZZZAUZLW9191234", } ], "modifyTimestamp": "2022-01-17T13:35:58.597569Z", } class Device(BaseModel): vehicle_id: str = Field(alias="vehicleId") modify_timestamp: datetime = Field(alias="modifyTimestamp") class DeviceList(RootModel[List[Device]]): pass # You can pass the list directly without the __root__ keyword argument validated_data = DeviceList(my_devices["devices"]) # Access the root list with .root, or iterate directly validated_data_list = [v.dict() for v in validated_data] print(validated_data) print(validated_data_list)
RootModel automatically implements sequence behavior, so you can iterate over it directly, and it also supports all standard Pydantic model features.
All these approaches eliminate the need to repeatedly reference __root__, making your code cleaner and more intuitive.
内容的提问来源于stack exchange,提问作者Cord Kaldemeyer

