3

I'm interested if there is any way to set an attribute on the Pydantic Model class the will only be used when constructing the output representation of the object. Something that works like Django Rest Framework SerializerMethodField. It is highly useful when you need to construct output depending on the environment or many other conditions.

  • https://stackoverflow.com/questions/63264888/pydantic-using-property-getter-decorator-for-a-field-with-an-alias sounds similar – Drdilyor May 28 '21 at 04:23
  • The problem with the solution outlined in @Drdilyor linked answer is that it assumes that the "SerializerMethod" method will only use values from the pydantic model. It cannot use fields from the ORM class that are not part of the Pydantic model. – Martin Faucheux Feb 15 '23 at 16:28

2 Answers2

0

It's possible to have a similar mechanism as django's SerializerMethodField by overriding pydantic BaseModel class

from copy import deepcopy
from pydantic import BaseModel, Field


class Person:
    def __init__(self, name, likes_cake):
        self.name = name
        self.likes_cake = likes_cake

class CustomPydanticModel(BaseModel):
    @classmethod
    def from_orm(cls, obj, getter_binding=None):
        getter_binding = getter_binding or {}
        obj = deepcopy(obj)

        for field in cls.__fields__:
            method = getter_binding.get(field)
            if method is None:
                method = getattr(cls, f"get_{field}", None)

            if method is not None and callable(method):
                setattr(obj, field, method(obj))

        return super().from_orm(obj)

class PersonModel(CustomPydanticModel):
    name: str
    status: str | None = None

    @staticmethod
    def get_status(obj):
        return "I like cake" if obj.likes_cake else "Hello"

    class Config:
        orm_mode = True

obj = Person("Patrick", True)
pydantic_obj = PersonModel.from_orm(obj)

Note that the get_status method should be static or classmethod

Martin Faucheux
  • 884
  • 9
  • 26
0

Had the same problem, solved it using @root_validator. Check my example:

class HireCandidateResponse(BaseModel):
    Id: str
    VacancyId: str
    Data: HireCandidateData
    hire_request_title: Optional[str]

    @root_validator
    def populate_hire_request_title(cls, values):
    vacancy_title = (
        values['Data'].ProfileId.Name if values['Data'] and values['Data'].ProfileId and values['Data'].ProfileId.Name else ''
    )
    org_unit_title = (
        values['Data'].OrgUnitId.Name if values['Data'] and values['Data'].OrgUnitId and values['Data'].OrgUnitId.Name else ''
    )
    values['hire_request_title'] = f'{vacancy_title} {org_unit_title}'
    return values
Artem Chege
  • 189
  • 2
  • 5