Un objet
pydantic
Dans l'exemple ci-dessous on veut associer un
User
Customer
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from pydantic import BaseModel class MinorUser(BaseModel): age: int name: str class MajorUser(BaseModel): age: int name: str class Customer(BaseModel): id: int user: MinorUser | MajorUser data = { "id": 12, "user": { "age": 40, "name": "David" } } customer = Customer(**data) print(customer) # id=12 user=MinorUser(age=40, name='David')
On remarque que user est de type
MinorUser
user: MajorUser | MinorUser
MajourUser
id=12 user=MajorUser(age=40, name='David')
Ce comportement n'est pas souhaitable, on cherche à obtenir le bon type en fonction d'un attribut particulier, ici l'âge.
On va ainsi préciser à
pydantic
age
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from typing import Annotated, List, Literal from pydantic import BaseModel, Field, field_validator class MinorUser(BaseModel): status: Literal["minor"] age: int name: str class MajorUser(BaseModel): status: Literal["major"] age: int name: str class Customer(BaseModel): id: int user: Annotated[MinorUser | MajorUser, Field(discriminator="status")] @field_validator("user", mode="before") @classmethod def validate(cls, user_data: User, validation_info) -> User: user_data["status"] = "major" if user_data["age"] >= 18 else "minor" return user_data data = { "id": 12, "user": { "age": 15, "name": "David" } } customer = Customer(**data) print(customer) # id=12 user=[MinorUser(status='minor', age=15, name='David')]
On remarque plusieurs points intéressants :
- On ajoute un
status
MinorUser
MajorUser
Customer
MajorUser
MinorUser
status
Literal
Customer
MajorUser
MinorUser
- Sur les objets
MinorUser
MajorUser
Literal
list
Literal
pydantic
- Sur
Customer
user: Annotated[MinorUser | MajorUser, Field(discriminator="status")]
Retrouver l'explication de
Annotated
Le champ
Field
pydantic
- L'utilisation d'un discriminant doit être obligatoirement fait en mode
before
pydantic
user
age
status
Copier le code
1 2 3 4 5 @field_validator("user", mode="before") @classmethod def validate(cls, users_data: User, validation_info) -> User: users_data["status"] = "major" if users_data["age"] >= 18 else "minor" return users_data
Maintenant si on injecte ce payload
Copier le code
1 2 3 4 5 6 7 data = { "id": 12, "user": { "age": 15, "name": "David" } }
On obtient
id=12 user=[MinorUser(status='minor', age=15, name='David')]
Et avec ce payload
Copier le code
1 2 3 4 5 6 7 data = { "id": 12, "user": { "age": 40, "name": "David" } }
On obtient
id=12 user=[MajorUser(status='minor', age=40, name='David')]
De cette manière on a un objet qui obtient le bon type en fonction d'une règle métier