Sérialisation
Au-delà de l'accès aux attributs du modèle directement via leurs noms de champs (par exemple model.foobar
), les modèles peuvent être convertis, sauvegardés, sérialisés et exportés de plusieurs manières.
!!! astuce "Sérialiser ou vider" Pydantic utilise les termes "sérialiser" et "vider" de manière interchangeable. Les deux font référence au processus de conversion d’un modèle en dictionnaire ou en chaîne codée JSON.
Outside of Pydantic, the word "serialize" usually refers to converting in-memory data into a string or bytes.
However, in the context of Pydantic, there is a very close relationship between converting an object from a more
structured form — such as a Pydantic model, a dataclass, etc. — into a less structured form comprised of
Python built-ins such as dict.
While we could (and on occasion, do) distinguish between these scenarios by using the word "dump" when converting to
primitives and "serialize" when converting to string, for practical purposes, we frequently use the word "serialize"
to refer to both of these situations, even though it does not always imply conversion to a string or bytes.
model.model_dump(...)
¶
??? API "Documentation API" pydantic.main.BaseModel.model_dump
Il s'agit du principal moyen de convertir un modèle en dictionnaire. Les sous-modèles seront convertis de manière récursive en dictionnaires.
!!! note La seule exception aux sous-modèles convertis en dictionnaires est que RootModel
et ses sous-classes verront la valeur du champ root
sauvegardée directement, sans dictionnaire d'encapsulage. Cela se fait également de manière récursive.
!!! note Vous pouvez utiliser des champs calculés pour inclure les données property
et de cached_property
dans la sortie model.model_dump(...)
.
Exemple:
from typing import Any, List, Optional
from pydantic import BaseModel, Field, Json
class BarModel(BaseModel):
whatever: int
class FooBarModel(BaseModel):
banana: Optional[float] = 1.1
foo: str = Field(serialization_alias='foo_alias')
bar: BarModel
m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})
# returns a dictionary:
print(m.model_dump())
#> {'banana': 3.14, 'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.model_dump(exclude={'foo', 'bar'}))
#> {'banana': 3.14}
print(m.model_dump(by_alias=True))
#> {'banana': 3.14, 'foo_alias': 'hello', 'bar': {'whatever': 123}}
print(
FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
exclude_unset=True
)
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
FooBarModel(banana=1.1, foo='hello', bar={'whatever': 123}).model_dump(
exclude_defaults=True
)
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
FooBarModel(foo='hello', bar={'whatever': 123}).model_dump(
exclude_defaults=True
)
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(
FooBarModel(banana=None, foo='hello', bar={'whatever': 123}).model_dump(
exclude_none=True
)
)
#> {'foo': 'hello', 'bar': {'whatever': 123}}
class Model(BaseModel):
x: List[Json[Any]]
print(Model(x=['{"a": 1}', '[1, 2]']).model_dump())
#> {'x': [{'a': 1}, [1, 2]]}
print(Model(x=['{"a": 1}', '[1, 2]']).model_dump(round_trip=True))
#> {'x': ['{"a":1}', '[1,2]']}
model.model_dump_json(...)
¶
??? API "Documentation API" pydantic.main.BaseModel.model_dump_json
La méthode .model_dump_json()
sérialise un modèle directement en une chaîne codée en JSON équivalente au résultat produit par .model_dump()
.
Voir arguments pour plus d'informations.
!!! note Pydantic peut sérialiser de nombreux types couramment utilisés en JSON qui seraient autrement incompatibles avec un simple json.dumps(foobar)
(par exemple datetime
, date
ou UUID
) .
from datetime import datetime
from pydantic import BaseModel
class BarModel(BaseModel):
whatever: int
class FooBarModel(BaseModel):
foo: datetime
bar: BarModel
m = FooBarModel(foo=datetime(2032, 6, 1, 12, 13, 14), bar={'whatever': 123})
print(m.model_dump_json())
#> {"foo":"2032-06-01T12:13:14","bar":{"whatever":123}}
print(m.model_dump_json(indent=2))
"""
{
"foo": "2032-06-01T12:13:14",
"bar": {
"whatever": 123
}
}
"""
dict(model)
et itération¶
Les modèles pydantiques peuvent également être convertis en dictionnaires à l'aide de dict(model)
, et vous pouvez également parcourir les champs d'un modèle à l'aide de for field_name, field_value in model:
. Avec cette approche, les valeurs brutes des champs sont renvoyées, donc les sous-modèles ne seront pas convertis en dictionnaires.
Exemple:
from pydantic import BaseModel
class BarModel(BaseModel):
whatever: int
class FooBarModel(BaseModel):
banana: float
foo: str
bar: BarModel
m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})
print(dict(m))
#> {'banana': 3.14, 'foo': 'hello', 'bar': BarModel(whatever=123)}
for name, value in m:
print(f'{name}: {value}')
#> banana: 3.14
#> foo: hello
#> bar: whatever=123
Notez également que RootModel
est converti en dictionnaire avec la clé 'root'
.
Sérialiseurs personnalisés¶
Pydantic fournit plusieurs [sérialiseurs fonctionnels][pydantic.function_serializers] pour personnaliser la façon dont un modèle est sérialisé dans un dictionnaire ou JSON.
- [
@field_serializer
][pydantic.function_serializers.field_serializer] - [
@model_serializer
][pydantic.function_serializers.model_serializer] - [
PlainSerializer
][pydantic.function_serializers.PlainSerializer] - [
WrapSerializer
][pydantic.function_serializers.WrapSerializer]
La sérialisation peut être personnalisée sur un champ à l'aide du décorateur [@field_serializer
][pydantic.function_serializers.field_serializer] et sur un modèle à l'aide du décorateur [@model_serializer
][pydantic.function_serializers.model_serializer].
from datetime import datetime, timedelta, timezone
from typing import Any, Dict
from pydantic import BaseModel, ConfigDict, field_serializer, model_serializer
class WithCustomEncoders(BaseModel):
model_config = ConfigDict(ser_json_timedelta='iso8601')
dt: datetime
diff: timedelta
@field_serializer('dt')
def serialize_dt(self, dt: datetime, _info):
return dt.timestamp()
m = WithCustomEncoders(
dt=datetime(2032, 6, 1, tzinfo=timezone.utc), diff=timedelta(hours=100)
)
print(m.model_dump_json())
#> {"dt":1969660800.0,"diff":"P4DT4H"}
class Model(BaseModel):
x: str
@model_serializer
def ser_model(self) -> Dict[str, Any]:
return {'x': f'serialized {self.x}'}
print(Model(x='test value').model_dump_json())
#> {"x":"serialized test value"}
!!! note Un seul sérialiseur peut également être appelé sur tous les champs en transmettant la valeur spéciale '*' au décorateur [@field_serializer
][pydantic.function_serializers.field_serializer].
De plus, [PlainSerializer
][pydantic.function_serializers.PlainSerializer] et [WrapSerializer
][pydantic.function_serializers.WrapSerializer] vous permettent d'utiliser une fonction pour modifier la sortie de la sérialisation.
Les deux sérialiseurs acceptent des arguments facultatifs, notamment:
return_type
spécifie le type de retour de la fonction. S'il est omis, il sera déduit de l'annotation de type.when_used
spécifie quand ce sérialiseur doit être utilisé. Accepte une chaîne avec les valeurs « toujours », « sauf-aucun », « json » et « json-sauf-aucun ». La valeur par défaut est «toujours».
PlainSerializer
utilise une fonction simple pour modifier la sortie de la sérialisation.
from typing_extensions import Annotated
from pydantic import BaseModel
from pydantic.functional_serializers import PlainSerializer
FancyInt = Annotated[
int, PlainSerializer(lambda x: f'{x:,}', return_type=str, when_used='json')
]
class MyModel(BaseModel):
x: FancyInt
print(MyModel(x=1234).model_dump())
#> {'x': 1234}
print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,234'}
WrapSerializer
reçoit les entrées brutes ainsi qu'une fonction de gestionnaire qui applique la logique de sérialisation standard et peut modifier la valeur résultante avant de la renvoyer comme sortie finale de la sérialisation.
from typing import Any
from typing_extensions import Annotated
from pydantic import BaseModel, SerializerFunctionWrapHandler
from pydantic.functional_serializers import WrapSerializer
def ser_wrap(v: Any, nxt: SerializerFunctionWrapHandler) -> str:
return f'{nxt(v + 1):,}'
FancyInt = Annotated[int, WrapSerializer(ser_wrap, when_used='json')]
class MyModel(BaseModel):
x: FancyInt
print(MyModel(x=1234).model_dump())
#> {'x': 1234}
print(MyModel(x=1234).model_dump(mode='json'))
#> {'x': '1,235'}
Remplacement du type de retour lors du dump d'un modèle¶
Alors que la valeur de retour de .model_dump()
peut généralement être décrite comme dict[str, Any]
, grâce à l'utilisation de @model_serializer
vous pouvez en fait lui faire renvoyer une valeur qui ne correspond pas à cette signature:
from pydantic import BaseModel, model_serializer
class Model(BaseModel):
x: str
@model_serializer
def ser_model(self) -> str:
return self.x
print(Model(x='not a dict').model_dump())
#> not a dict
Si vous souhaitez faire cela tout en obtenant une vérification de type appropriée pour cette méthode, vous pouvez remplacer .model_dump()
dans un bloc if TYPE_CHECKING:
:
from typing import TYPE_CHECKING, Any
from typing_extensions import Literal
from pydantic import BaseModel, model_serializer
class Model(BaseModel):
x: str
@model_serializer
def ser_model(self) -> str:
return self.x
if TYPE_CHECKING:
# Ensure type checkers see the correct return type
def model_dump(
self,
*,
mode: Literal['json', 'python'] | str = 'python',
include: Any = None,
exclude: Any = None,
by_alias: bool = False,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
round_trip: bool = False,
warnings: bool = True,
) -> str: ...
Cette astuce est en fait utilisée dans RootModel
précisément dans ce but.
Sérialisation des sous-classes¶
Sous-classes de types standards¶
Les sous-classes des types standards sont automatiquement vidées comme leurs super-classes:
from datetime import date, timedelta
from typing import Any, Type
from pydantic_core import core_schema
from pydantic import BaseModel, GetCoreSchemaHandler
class DayThisYear(date):
"""
Contrived example of a special type of date that
takes an int and interprets it as a day in the current year
"""
@classmethod
def __get_pydantic_core_schema__(
cls, source: Type[Any], handler: GetCoreSchemaHandler
) -> core_schema.CoreSchema:
return core_schema.no_info_after_validator_function(
cls.validate,
core_schema.int_schema(),
serialization=core_schema.format_ser_schema('%Y-%m-%d'),
)
@classmethod
def validate(cls, v: int):
return date(2023, 1, 1) + timedelta(days=v)
class FooModel(BaseModel):
date: DayThisYear
m = FooModel(date=300)
print(m.model_dump_json())
#> {"date":"2023-10-28"}
Instances de sous-classe pour les champs de BaseModel
, classes de données, TypedDict
¶
Lors de l'utilisation de champs dont les annotations sont elles-mêmes des types de type struct (par exemple, sous-classes BaseModel
, classes de données, etc.), le comportement par défaut est de sérialiser la valeur de l'attribut comme s'il s'agissait d'une instance du type annoté, même s'il s'agit d'une sous-classe. Plus précisément, seuls les champs du type annoté seront inclus dans l'objet dumpé:
from pydantic import BaseModel
class User(BaseModel):
name: str
class UserLogin(User):
password: str
class OuterModel(BaseModel):
user: User
user = UserLogin(name='pydantic', password='hunter2')
m = OuterModel(user=user)
print(m)
#> user=UserLogin(name='pydantic', password='hunter2')
print(m.model_dump()) # note: the password field is not included
#> {'user': {'name': 'pydantic'}}
!!! avertissement "Avertissement de migration" Ce comportement est différent de la façon dont les choses fonctionnaient dans Pydantic V1, où nous incluions toujours tous les champs (sous-classe) lors du dumping récursif de modèles vers des dicts. La motivation derrière ce changement de comportement est qu'il permet de garantir que vous savez précisément quels champs peuvent être inclus lors de la sérialisation, même si les sous-classes sont transmises lors de l'instanciation de l'objet. En particulier, cela peut aider à éviter les surprises lors de l'ajout d'informations sensibles telles que des secrets en tant que champs de sous-classes.
Sérialisation avec duck-typing 🦆¶
Qu'est-ce que la sérialisation avec le typage canard ?
Duck-typing serialization is the behavior of serializing an object based on the fields present in the object itself, rather than the fields present in the schema of the object. This means that when an object is serialized, fields present in a subclass, but not in the original schema, will be included in the serialized output.
This behavior was the default in Pydantic V1, but was changed in V2 to help ensure that you know precisely which fields would be included when serializing, even if subclasses get passed when instantiating the object. This helps prevent security risks when serializing subclasses with sensitive information, for example.
Si vous souhaitez un comportement de sérialisation de type canard de style v1, vous pouvez utiliser un paramètre d'exécution ou annoter des types individuels.
- Niveau champ/type: utiliser l'annotation
SerializeAsAny
- Niveau d'exécution: utilisez l'indicateur
serialize_as_any
lors de l'appelmodel_dump()
oumodel_dump_json()
Nous discutons de ces options ci-dessous plus en détail:
Annotation SerializeAsAny
:¶
Si vous souhaitez un comportement de sérialisation par type de canard, cela peut être fait en utilisant l'annotation SerializeAsAny
sur un type:
from pydantic import BaseModel, SerializeAsAny
class User(BaseModel):
name: str
class UserLogin(User):
password: str
class OuterModel(BaseModel):
as_any: SerializeAsAny[User]
as_user: User
user = UserLogin(name='pydantic', password='password')
print(OuterModel(as_any=user, as_user=user).model_dump())
"""
{
'as_any': {'name': 'pydantic', 'password': 'password'},
'as_user': {'name': 'pydantic'},
}
"""
Lorsqu'un champ est annoté comme SerializeAsAny[<SomeType>]
, le comportement de validation sera le même que s'il était annoté comme <SomeType>
, et les vérificateurs de type comme mypy traiteront également l'attribut comme ayant le type approprié. Mais lors de la sérialisation, le champ sera sérialisé comme si l'indication de type pour le champ était Any
, d'où vient le nom.
paramètre d'exécution serialize_as_any
¶
Le paramètre d'exécution serialize_as_any
peut être utilisé pour sérialiser les données du modèle avec ou sans comportement de sérialisation de type canard. serialize_as_any
peut être transmis comme argument de mot-clé aux méthodes model_dump()
et model_dump_json
des BaseModel
et RootModel
. Il peut également être transmis comme argument de mot-clé aux méthodes dump_python()
et dump_json()
des TypeAdapter
.
Si serialize_as_any
est défini sur True
, le modèle sera sérialisé en utilisant le comportement de sérialisation de type canard, ce qui signifie que le modèle ignorera le schéma et demandera à la place à l'objet lui-même comment il doit être sérialisé. En particulier, cela signifie que lorsque les sous-classes du modèle sont sérialisées, les champs présents dans la sous-classe mais pas dans le schéma d'origine seront inclus.
Si serialize_as_any
est défini sur False
(ce qui est la valeur par défaut), le modèle sera sérialisé à l'aide du schéma, ce qui signifie que les champs présents dans une sous-classe mais pas dans le schéma d'origine seront ignorés.
!!! question «Pourquoi ce drapeau est-il utile?» Parfois, vous souhaitez vous assurer que quels que soient les champs ajoutés dans les sous-classes, l'objet sérialisé n'aura que les champs répertoriés dans la définition de type d'origine. Cela peut être utile si vous ajoutez quelque chose comme un champ password: str
dans une sous-classe que vous ne souhaitez pas inclure accidentellement dans la sortie sérialisée.
Par exemple:
from pydantic import BaseModel
class User(BaseModel):
name: str
class UserLogin(User):
password: str
class OuterModel(BaseModel):
user1: User
user2: User
user = UserLogin(name='pydantic', password='password')
outer_model = OuterModel(user1=user, user2=user)
print(outer_model.model_dump(serialize_as_any=True)) # (1)!
"""
{
'user1': {'name': 'pydantic', 'password': 'password'},
'user2': {'name': 'pydantic', 'password': 'password'},
}
"""
print(outer_model.model_dump(serialize_as_any=False)) # (2)!
#> {'user1': {'name': 'pydantic'}, 'user2': {'name': 'pydantic'}}
- Avec
serialize_as_any
défini surTrue
, le résultat correspond à celui de la V1. - Avec
serialize_as_any
défini surFalse
(valeur par défaut de la V2), les champs présents sur la sous-classe, mais pas sur la classe de base, ne sont pas inclus dans la sérialisation.
Ce paramètre prend même effet avec les modèles imbriqués et récursifs. Par exemple:
from typing import List
from pydantic import BaseModel
class User(BaseModel):
name: str
friends: List['User']
class UserLogin(User):
password: str
class OuterModel(BaseModel):
user: User
user = UserLogin(
name='samuel',
password='pydantic-pw',
friends=[UserLogin(name='sebastian', password='fastapi-pw', friends=[])],
)
print(OuterModel(user=user).model_dump(serialize_as_any=True)) # (1)!
"""
{
'user': {
'name': 'samuel',
'friends': [
{'name': 'sebastian', 'friends': [], 'password': 'fastapi-pw'}
],
'password': 'pydantic-pw',
}
}
"""
print(OuterModel(user=user).model_dump(serialize_as_any=False)) # (2)!
"""
{'user': {'name': 'samuel', 'friends': [{'name': 'sebastian', 'friends': []}]}}
"""
- Même les instances de modèle
User
imbriquées sont vidées avec des champs propres aux sous-classesUser
. - Même les instances de modèle
User
imbriquées sont vidées sans champs propres aux sous-classesUser
.
!!! note Le comportement de l'indicateur d'exécution serialize_as_any
est presque le même que celui de l'annotation SerializeAsAny
. Il existe quelques différences nuancées que nous nous efforçons de résoudre, mais pour la plupart, vous pouvez vous attendre au même comportement de la part des deux. En savoir plus sur les différences dans ce numéro actif
Remplacement de la valeur par défaut serialize_as_any
(False)¶
Vous pouvez remplacer le paramètre par défaut de serialize_as_any
en configurant une sous-classe de BaseModel
qui remplace la valeur par défaut de l'argument serialize_as_any
en model_dump()
et model_dump_json()
, puis l'utiliser comme classe de base (au lieu de pydantic.BaseModel
) pour n'importe quel modèle que vous souhaitez avoir ce comportement par défaut.
Par exemple, vous pouvez procéder comme suit si vous souhaitez utiliser la sérialisation par type de canard par défaut:
from typing import Any, Dict
from pydantic import BaseModel, SecretStr
class MyBaseModel(BaseModel):
def model_dump(self, **kwargs) -> Dict[str, Any]:
return super().model_dump(serialize_as_any=True, **kwargs)
def model_dump_json(self, **kwargs) -> str:
return super().model_dump_json(serialize_as_any=True, **kwargs)
class User(MyBaseModel):
name: str
class UserInfo(User):
password: SecretStr
class OuterModel(MyBaseModel):
user: User
u = OuterModel(user=UserInfo(name='John', password='secret_pw'))
print(u.model_dump_json()) # (1)!
#> {"user":{"name":"John","password":"**********"}}
- Par défaut,
model_dump_json
utilisera le comportement de sérialisation par type de canard, ce qui signifie que le champpassword
est inclus dans la sortie.
pickle.dumps(model)
¶
Les modèles Pydantic prennent en charge un décapage et un décapage efficaces.
import pickle
from pydantic import BaseModel
class FooBarModel(BaseModel):
a: str
b: int
m = FooBarModel(a='hello', b=123)
print(m)
#> a='hello' b=123
data = pickle.dumps(m)
print(data[:20])
#> b'\x80\x04\x95\x95\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main_'
m2 = pickle.loads(data)
print(m2)
#> a='hello' b=123
Inclure et exclure avancés¶
Les méthodes model_dump
et model_dump_json
prennent en charge les arguments include
et exclude
qui peuvent être des ensembles ou des dictionnaires. Cela permet une sélection imbriquée des champs à exporter:
from pydantic import BaseModel, SecretStr
class User(BaseModel):
id: int
username: str
password: SecretStr
class Transaction(BaseModel):
id: str
user: User
value: int
t = Transaction(
id='1234567890',
user=User(id=42, username='JohnDoe', password='hashedpassword'),
value=9876543210,
)
# using a set:
print(t.model_dump(exclude={'user', 'value'}))
#> {'id': '1234567890'}
# using a dict:
print(t.model_dump(exclude={'user': {'username', 'password'}, 'value': True}))
#> {'id': '1234567890', 'user': {'id': 42}}
print(t.model_dump(include={'id': True, 'user': {'id'}}))
#> {'id': '1234567890', 'user': {'id': 42}}
Le True
indique que nous souhaitons exclure ou inclure une clé entière, comme si nous l'incluions dans un ensemble. Cela peut être fait à n’importe quel niveau de profondeur.
Des précautions particulières doivent être prises lors de l'inclusion ou de l'exclusion de champs d'une liste ou d'un tuple de sous-modèles ou de dictionnaires. Dans ce scénario, model_dump
et les méthodes associées attendent des clés entières pour l'inclusion ou l'exclusion élément par élément. Pour exclure un champ de chaque membre d'une liste ou d'un tuple, la clé du dictionnaire '__all__'
peut être utilisée, comme indiqué ici:
import datetime
from typing import List
from pydantic import BaseModel, SecretStr
class Country(BaseModel):
name: str
phone_code: int
class Address(BaseModel):
post_code: int
country: Country
class CardDetails(BaseModel):
number: SecretStr
expires: datetime.date
class Hobby(BaseModel):
name: str
info: str
class User(BaseModel):
first_name: str
second_name: str
address: Address
card_details: CardDetails
hobbies: List[Hobby]
user = User(
first_name='John',
second_name='Doe',
address=Address(
post_code=123456, country=Country(name='USA', phone_code=1)
),
card_details=CardDetails(
number='4212934504460000', expires=datetime.date(2020, 5, 1)
),
hobbies=[
Hobby(name='Programming', info='Writing code and stuff'),
Hobby(name='Gaming', info='Hell Yeah!!!'),
],
)
exclude_keys = {
'second_name': True,
'address': {'post_code': True, 'country': {'phone_code'}},
'card_details': True,
# You can exclude fields from specific members of a tuple/list by index:
'hobbies': {-1: {'info'}},
}
include_keys = {
'first_name': True,
'address': {'country': {'name'}},
'hobbies': {0: True, -1: {'name'}},
}
# would be the same as user.model_dump(exclude=exclude_keys) in this case:
print(user.model_dump(include=include_keys))
"""
{
'first_name': 'John',
'address': {'country': {'name': 'USA'}},
'hobbies': [
{'name': 'Programming', 'info': 'Writing code and stuff'},
{'name': 'Gaming'},
],
}
"""
# To exclude a field from all members of a nested list or tuple, use "__all__":
print(user.model_dump(exclude={'hobbies': {'__all__': {'info'}}}))
"""
{
'first_name': 'John',
'second_name': 'Doe',
'address': {
'post_code': 123456,
'country': {'name': 'USA', 'phone_code': 1},
},
'card_details': {
'number': SecretStr('**********'),
'expires': datetime.date(2020, 5, 1),
},
'hobbies': [{'name': 'Programming'}, {'name': 'Gaming'}],
}
"""
Il en va de même pour la méthode model_dump_json
.
Inclure et exclure au niveau du modèle et du champ¶
En plus des arguments explicites exclude
et include
passés aux méthodes model_dump
et model_dump_json
, nous pouvons également transmettre les arguments exclude: bool
directement au constructeur Field
:
La définition exclude
sur le constructeur de champ ( Field(..., exclude=True)
) est prioritaire sur l' exclude
/ include
sur model_dump
et model_dump_json
:
from pydantic import BaseModel, Field, SecretStr
class User(BaseModel):
id: int
username: str
password: SecretStr = Field(..., exclude=True)
class Transaction(BaseModel):
id: str
value: int = Field(exclude=True)
t = Transaction(
id='1234567890',
value=9876543210,
)
print(t.model_dump())
#> {'id': '1234567890'}
print(t.model_dump(include={'id': True, 'value': True})) # (1)!
#> {'id': '1234567890'}
value
exclue de la sortie car elle a été exclue dansField
.
Cela étant dit, la définition de exclude
sur le constructeur de champ ( Field(..., exclude=True)
) n'a pas la priorité sur les paramètres exclude_unset
, exclude_none
et exclude_default
sur model_dump
et model_dump_json
:
from typing import Optional
from pydantic import BaseModel, Field
class Person(BaseModel):
name: str
age: Optional[int] = Field(None, exclude=False)
person = Person(name='Jeremy')
print(person.model_dump())
#> {'name': 'Jeremy', 'age': None}
print(person.model_dump(exclude_none=True)) # (1)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_unset=True)) # (2)!
#> {'name': 'Jeremy'}
print(person.model_dump(exclude_defaults=True)) # (3)!
#> {'name': 'Jeremy'}
age
exclu de la sortie carexclude_none
a été défini surTrue
etage
estNone
.age
exclu de la sortie carexclude_unset
était défini surTrue
etage
n'était pas défini dans le constructeur Person.age
exclu de la sortie carexclude_defaults
a été défini surTrue
etage
prend la valeur par défaut deNone
.
Contexte de sérialisation¶
Vous pouvez transmettre un objet contextuel aux méthodes de sérialisation accessibles depuis l'argument info
vers les fonctions de sérialisation décorées. Ceci est utile lorsque vous devez mettre à jour dynamiquement le comportement de sérialisation pendant l'exécution. Par exemple, si vous souhaitez qu'un champ soit vidé en fonction d'un ensemble de valeurs autorisées contrôlables dynamiquement, cela peut être fait en transmettant les valeurs autorisées par contexte:
from pydantic import BaseModel, SerializationInfo, field_serializer
class Model(BaseModel):
text: str
@field_serializer('text')
def remove_stopwords(self, v: str, info: SerializationInfo):
context = info.context
if context:
stopwords = context.get('stopwords', set())
v = ' '.join(w for w in v.split() if w.lower() not in stopwords)
return v
model = Model.model_construct(**{'text': 'This is an example document'})
print(model.model_dump()) # no context
#> {'text': 'This is an example document'}
print(model.model_dump(context={'stopwords': ['this', 'is', 'an']}))
#> {'text': 'example document'}
print(model.model_dump(context={'stopwords': ['document']}))
#> {'text': 'This is an example'}
De même, vous pouvez utiliser un contexte pour la validation .
model_copy(...)
¶
??? API "Documentation API" pydantic.main.BaseModel.model_copy
model_copy()
permet de dupliquer les modèles (avec des mises à jour facultatives), ce qui est particulièrement utile lorsque l'on travaille avec des modèles gelés.
Exemple:
from pydantic import BaseModel
class BarModel(BaseModel):
whatever: int
class FooBarModel(BaseModel):
banana: float
foo: str
bar: BarModel
m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})
print(m.model_copy(update={'banana': 0}))
#> banana=0 foo='hello' bar=BarModel(whatever=123)
print(id(m.bar) == id(m.model_copy().bar))
#> True
# normal copy gives the same object reference for bar
print(id(m.bar) == id(m.model_copy(deep=True).bar))
#> False
# deep copy gives a new object reference for `bar`
本文总阅读量次