Search code examples
pythonpydantic

Pydantic: How do I represent a list of objects as dict (serialize list as dict)


In Pydantic I want to represent a list of items as a dictionary. In that dictionary I want the key to be the id of the Item to be the key of the dictionary. I read the documentation on Serialization, on Mapping types and on Sequences.

However, I didn't find a way to create such a representation.

I want these in my api to have easy access from generated api clients.

class Item(BaseModel):
    uid: UUID = Field(default_factory=uuid4)
    updated: datetime = Field(default_factory=datetime_now)
    ...


class ResponseModel:
    ...


print ResponseModel.model_dump()
#> {
        "67C812D7-B039-433C-B925-CA21A1FBDB23": {
            "uid": "67C812D7-B039-433C-B925-CA21A1FBDB23", 
            "updated": "2024-05-02 20:24:00"
        },{
        "D39A8EF1-E520-4946-A818-9FA8664F63F6": {
            "uid": "D39A8EF1-E520-4946-A818-9FA8664F63F6",
            "updated":"2024-05-02 20:25:00"
        }
    }

Solution

  • You can use PlainSerializer(docs) to change the serialization of a field, e.g., to change the desired output format of datetime:

    from pydantic import Field, BaseModel, PlainSerializer
    from uuid import UUID, uuid4
    from datetime import datetime
    from typing import Annotated
    
    
    CustomUUID = Annotated[
        UUID, PlainSerializer(lambda v: str(v), return_type=str)
    ]
    CustomDatetime = Annotated[
        datetime,
        PlainSerializer(
            lambda v: v.strftime("%Y-%m-%d %H:%M:%S"), return_type=str
        ),
    ]
    
    class Item(BaseModel):
        uid: CustomUUID = Field(default_factory=uuid4)
        updated: CustomDatetime = Field(default_factory=datetime.now)
        
    Item().model_dump()
    # {'uid': '7b2b8e32-15c7-48b8-9ddb-2d25cc61bc61',
    #  'updated': '2024-05-03 00:13:38'}
    

    Then you can use model_serializer (docs) to serialize the ResponseModel:

    from pydantic import Field, BaseModel, model_serializer, PlainSerializer
    from uuid import UUID, uuid4
    from datetime import datetime
    from typing import Annotated
    
    CustomUUID = Annotated[
        UUID, PlainSerializer(lambda v: str(v), return_type=str)
    ]
    CustomDatetime = Annotated[
        datetime,
        PlainSerializer(
            lambda v: v.strftime("%Y-%m-%d %H:%M:%S"), return_type=str
        ),
    ]
    
    
    class Item(BaseModel):
        uid: CustomUUID = Field(default_factory=uuid4)
        updated: CustomDatetime = Field(default_factory=datetime.now)
    
    class ResponseModel(BaseModel):
        items: list[Item]
    
        @model_serializer
        def serialize_model(self):
            return {str(item.uid): item.model_dump() for item in self.items}
    
    ResponseModel(items=[Item() for i in range(2)]).model_dump()
    
    # {
    #     "e41c4446-60e3-45c5-ab0a-a25f15febac4": {
    #         "uid": "e41c4446-60e3-45c5-ab0a-a25f15febac4",
    #         "updated": "2024-05-03 00:14:58",
    #     },
    #     "e07d4152-755a-4a0f-9312-29d343bda883": {
    #         "uid": "e07d4152-755a-4a0f-9312-29d343bda883",
    #         "updated": "2024-05-03 00:14:58",
    #     },
    # }