pydantic
Copier le code
1 2 3 class User(BaseModel): id: int name: str
Sa grande force est également la possibilité de faire de la validation de ses données. Ainsi en affectant à
User
id
UUID
pydantic
Copier le code
1 2 3 4 5 6 7 8 9 10 11 class User(BaseModel): id: UUID name: str User(id="7682927b-4e56-4769-a2dc-058bc4296f50", name="David") User(id=12, name="David") # Lève l'erreur ci-dessous # pydantic_core._pydantic_core.ValidationError: 1 validation error for User # id # UUID input should be a string, bytes or UUID object [type=uuid_type, input_value=12, input_type=int]
Pour des besoins spécifique on peut mettre en place une vérification statique comme
Copier le code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from uuid import UUID from pydantic import BaseModel, Field class User(BaseModel): id: UUID name: str email: str = Field(pattern=r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$") User(id="7682927b-4e56-4769-a2dc-058bc4296f50", name="David", email="david@domain.fr") User(id="7682927b-4e56-4769-a2dc-058bc4296f50", name="David", email="david") # pydantic_core._pydantic_core.ValidationError: 1 validation error for User # email # String should match pattern '^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$' [type=string_pattern_mismatch, input_value='david', input_type=str]
On pourrait également vouloir filtrer sur les personnes majeures. Dans ce cas on introduit une logique, on va s'orienter vers un validateur
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 35 36 37 38 39 40 from datetime import date from uuid import UUID from pydantic import BaseModel, field_validator class User(BaseModel): id: UUID name: str birthday: date @field_validator("birthday") @classmethod def check_major(cls, value: date) -> date: today = date.today() age = ( today.year - value.year - ((today.month, today.day) < (value.month, value.day)) ) if age < 18: raise ValueError("L'utilisateur doit être majeur") return value User( id="7682927b-4e56-4769-a2dc-058bc4296f50", name="David", birthday=date(day=1, month=1, year=2000), ) User( id="7682927b-4e56-4769-a2dc-058bc4296f50", name="David", birthday=date(day=1, month=1, year=2020), ) # pydantic_core._pydantic_core.ValidationError: 1 validation error for User # birthday # Value error, L'utilisateur doit être majeur [type=value_error, input_value=datetime.date(2020, 1, 1), input_type=date]
Solution avec Annotated
Et après ce rappel, j'aimerais vous le présenter sous une autre approche. En effet, sur le dernier cas de
birthday
Il peut être intéressant de déporter cela pour avoir une séparation de la logique (aka SOLID)
C'est à ce moment que l'on va pouvoir utiliser
Annotated
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 35 36 37 38 39 from typing import Annotated from pydantic import AfterValidator # adult.py from datetime import date from typing import Annotated from uuid import UUID from pydantic import AfterValidator, BaseModel def validate_age(value: date): if (date.today().year - value.year) < 18: raise ValueError("L'utilisateur doit être majeur") return value LegalAgeDate = Annotated[date, AfterValidator(validate_age)] # user.py class User(BaseModel): id: UUID birthday: LegalAgeDate User( id="7682927b-4e56-4769-a2dc-058bc4296f50", name="David", birthday=date(day=1, month=1, year=2000), ) User( id="7682927b-4e56-4769-a2dc-058bc4296f50", name="David", birthday=date(day=1, month=1, year=2020), ) pydantic_core._pydantic_core.ValidationError: 1 validation error for User birthday Value error, L'utilisateur doit être majeur [type=value_error, input_value=datetime.date(2020, 1, 1), input_type=date]
Je trouve cette solution très élégante et pythonique. On se rapproche ainsi des concepts du DDD où ce
LegalAgeDate