??? api "API-documentatie" pydantic.main.BaseModel
Een van de belangrijkste manieren om schema's in Pydantic te definiëren is via modellen. Modellen zijn eenvoudigweg klassen die overerven van pydantic.BaseModel
en velden definiëren als geannoteerde attributen.
U kunt modellen beschouwen als vergelijkbaar met structuren in talen als C, of als de vereisten van een enkel eindpunt in een API.
Modellen hebben veel overeenkomsten met de dataklassen van Python, maar zijn ontworpen met enkele subtiele maar toch belangrijke verschillen die bepaalde workflows stroomlijnen die verband houden met validatie, serialisatie en het genereren van JSON-schema's. Meer informatie hierover vindt u in de sectie Dataclasses van de documentatie.
Niet-vertrouwde gegevens kunnen aan een model worden doorgegeven en, na parsering en validatie, garandeert Pydantic dat de velden van de resulterende modelinstantie zullen voldoen aan de veldtypen die in het model zijn gedefinieerd.
!!! opmerking "Validatie — een opzettelijk verkeerde benaming" ### TL;DR
We use the term "validation" to refer to the process of instantiating a model (or other type) that adheres to specified types and
constraints. This task, which Pydantic is well known for, is most widely recognized as "validation" in colloquial terms,
even though in other contexts the term "validation" may be more restrictive.
---
### The long version
The potential confusion around the term "validation" arises from the fact that, strictly speaking, Pydantic's
primary focus doesn't align precisely with the dictionary definition of "validation":
> ### validation
> _noun_
> the action of checking or proving the validity or accuracy of something.
In Pydantic, the term "validation" refers to the process of instantiating a model (or other type) that adheres to specified
types and constraints. Pydantic guarantees the types and constraints of the output, not the input data.
This distinction becomes apparent when considering that Pydantic's `ValidationError` is raised
when data cannot be successfully parsed into a model instance.
While this distinction may initially seem subtle, it holds practical significance.
In some cases, "validation" goes beyond just model creation, and can include the copying and coercion of data.
This can involve copying arguments passed to the constructor in order to perform coercion to a new type
without mutating the original input data. For a more in-depth understanding of the implications for your usage,
refer to the [Data Conversion](#data-conversion) and [Attribute Copies](#attribute-copies) sections below.
In essence, Pydantic's primary goal is to assure that the resulting structure post-processing (termed "validation")
precisely conforms to the applied type hints. Given the widespread adoption of "validation" as the colloquial term
for this process, we will consistently use it in our documentation.
While the terms "parse" and "validation" were previously used interchangeably, moving forward, we aim to exclusively employ "validate",
with "parse" reserved specifically for discussions related to [JSON parsing](../concepts/json.md).
Basismodelgebruik¶
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str = 'Jane Doe'
In dit voorbeeld is User
een model met twee velden:
id
, wat een geheel getal is en vereist isname
, wat een tekenreeks is en niet vereist is (deze heeft een standaardwaarde).
gebruiker = Gebruiker(id='123')
In dit voorbeeld is user
een exemplaar van User
. Initialisatie van het object zal alle parsering en validatie uitvoeren. Als er geen ValidationError
wordt gegenereerd, weet u dat de resulterende modelinstantie geldig is.
assert user.id == 123
assert isinstance(user.id, int)
# Note that '123' was coerced to an int and its value is 123
Meer details over de dwanglogica van pydantic zijn te vinden in Dataconversie . Velden van een model zijn toegankelijk als normale attributen van het user
. De string '123'
is omgezet in een int volgens het veldtype.
assert user.name == 'Jane Doe'
name
is niet ingesteld toen user
werd geïnitialiseerd, dus deze heeft de standaardwaarde.
assert user.model_fields_set == {'id'}
De velden die zijn opgegeven toen de gebruiker werd geïnitialiseerd.
assert user.model_dump() == {'id': 123, 'name': 'Jane Doe'}
.model_dump()
of dict(user)
levert een dictaat van velden op, maar .model_dump()
kan talloze andere argumenten aannemen. (Merk op dat dict(user)
geneste modellen niet recursief naar dicts zal converteren, maar .model_dump()
wel.)
user.id = 321
assert user.id == 321
Standaard zijn modellen veranderlijk en kunnen veldwaarden worden gewijzigd via attribuuttoewijzing.
Modelmethoden en eigenschappen¶
Het bovenstaande voorbeeld toont slechts het topje van de ijsberg van wat modellen kunnen doen. Modellen beschikken over de volgende methoden en attributen:
model_computed_fields
: een woordenboek met de berekende velden van deze modelinstantie.model_construct()
: een klassenmethode voor het maken van modellen zonder validatie uit te voeren. Zie Modellen maken zonder validatie .model_copy()
: retourneert een kopie (standaard een ondiepe kopie) van het model. Zie Serialisatie .model_dump()
: retourneert een woordenboek met de velden en waarden van het model. Zie Serialisatie .model_dump_json()
: retourneert een JSON-tekenreeksrepresentatie vanmodel_dump()
. Zie Serialisatie .model_extra
: laat extra velden instellen tijdens validatie.model_fields_set
: set velden die zijn ingesteld toen de modelinstantie werd geïnitialiseerd.model_json_schema()
: retourneert een jsonable woordenboek dat het model vertegenwoordigt als JSON-schema. Zie JSON-schema .model_parametrized_name()
: bereken de klassenaam voor parametrisaties van generieke klassen.model_post_init()
: voer aanvullende initialisatie uit nadat het model is geïnitialiseerd.model_rebuild()
: bouw het modelschema opnieuw op, dat ook het bouwen van recursieve generieke modellen ondersteunt. Zie Modelschema opnieuw opbouwen .model_validate()
: een hulpprogramma voor het laden van elk object in een model. Zie Helperfuncties .model_validate_json()
: een hulpprogramma voor het valideren van de gegeven JSON-gegevens tegen het Pydantic-model. Zie Helperfuncties .
!!! opmerking Zie BaseModel
voor de klassendefinitie, inclusief een volledige lijst met methoden en attributen.
!!! tip Zie Wijzigingen in pydantic.BaseModel
in de Migratiehandleiding voor details over wijzigingen ten opzichte van Pydantic V1.
Geneste modellen¶
Complexere hiërarchische datastructuren kunnen worden gedefinieerd door modellen zelf te gebruiken als typen in annotaties.
from typing import List, Optional
from pydantic import BaseModel
class Foo(BaseModel):
count: int
size: Optional[float] = None
class Bar(BaseModel):
apple: str = 'x'
banana: str = 'y'
class Spam(BaseModel):
foo: Foo
bars: List[Bar]
m = Spam(foo={'count': 4}, bars=[{'apple': 'x1'}, {'apple': 'x2'}])
print(m)
"""
foo=Foo(count=4, size=None) bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y')]
"""
print(m.model_dump())
"""
{
'foo': {'count': 4, 'size': None},
'bars': [{'apple': 'x1', 'banana': 'y'}, {'apple': 'x2', 'banana': 'y'}],
}
"""
Zie uitgestelde annotaties voor naar zichzelf verwijzende modellen.
!!! note Let bij het definiëren van uw modellen op naamconflicten tussen uw veldnaam en het type ervan, een eerder gedefinieerd model of een geïmporteerde bibliotheek.
For example, the following would yield a validation error:
```py
from typing import Optional
from pydantic import BaseModel
class Boo(BaseModel):
int: Optional[int] = None
m = Boo(int=123) # errors
```
An error occurs since the field `int` is set to a default value of `None` and has the exact same name as its type, so both are interpreted to be `None`.
Modelschema opnieuw opbouwen¶
Het modelschema kan opnieuw worden opgebouwd met behulp van model_rebuild()
. Dit is handig voor het bouwen van recursieve generieke modellen.
from pydantic import BaseModel, PydanticUserError
class Foo(BaseModel):
x: 'Bar'
try:
Foo.model_json_schema()
except PydanticUserError as e:
print(e)
"""
`Foo` is not fully defined; you should define `Bar`, then call `Foo.model_rebuild()`.
For further information visit https://errors.pydantic.dev/2/u/class-not-fully-defined
"""
class Bar(BaseModel):
pass
Foo.model_rebuild()
print(Foo.model_json_schema())
"""
{
'$defs': {'Bar': {'properties': {}, 'title': 'Bar', 'type': 'object'}},
'properties': {'x': {'$ref': '#/$defs/Bar'}},
'required': ['x'],
'title': 'Foo',
'type': 'object',
}
"""
Pydantic probeert automatisch te bepalen wanneer dit nodig is en geeft een foutmelding als dit niet is gebeurd, maar misschien wilt u model_rebuild()
proactief aanroepen als u te maken heeft met recursieve modellen of generieke modellen.
In V2 verving model_rebuild()
update_forward_refs()
uit V1. Er zijn enkele kleine verschillen met het nieuwe gedrag. De grootste verandering is dat bij het aanroepen van model_rebuild()
op het buitenste model, het een kernschema bouwt dat wordt gebruikt voor validatie van het hele model (geneste modellen en alles), dus alle typen überhaupt niveaus moeten gereed zijn voordat model_rebuild()
wordt aangeroepen.
Willekeurige klasse-instanties¶
(Voorheen bekend als "ORM-modus"/ from_orm
.)
Pydantic-modellen kunnen ook worden gemaakt op basis van willekeurige klasse-instanties door de instantieattributen te lezen die overeenkomen met de modelveldnamen. Een veel voorkomende toepassing van deze functionaliteit is de integratie met object-relationele mappings (ORM's).
Om dit te doen, stelt u het config-attribuut in model_config['from_attributes'] = True
. Zie Model Config en ConfigDict voor meer informatie.
In het voorbeeld hier wordt SQLAlchemy gebruikt, maar dezelfde aanpak zou voor elke ORM moeten werken.
from typing import List
from sqlalchemy import Column, Integer, String
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.orm import declarative_base
from typing_extensions import Annotated
from pydantic import BaseModel, ConfigDict, StringConstraints
Base = declarative_base()
class CompanyOrm(Base):
__tablename__ = 'companies'
id = Column(Integer, primary_key=True, nullable=False)
public_key = Column(String(20), index=True, nullable=False, unique=True)
name = Column(String(63), unique=True)
domains = Column(ARRAY(String(255)))
class CompanyModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
public_key: Annotated[str, StringConstraints(max_length=20)]
name: Annotated[str, StringConstraints(max_length=63)]
domains: List[Annotated[str, StringConstraints(max_length=255)]]
co_orm = CompanyOrm(
id=123,
public_key='foobar',
name='Testing',
domains=['example.com', 'foobar.com'],
)
print(co_orm)
#> <__main__.CompanyOrm object at 0x0123456789ab>
co_model = CompanyModel.model_validate(co_orm)
print(co_model)
"""
id=123 public_key='foobar' name='Testing' domains=['example.com', 'foobar.com']
"""
Gereserveerde namen¶
Mogelijk wilt u een Column
een naam geven naar een gereserveerd SQLAlchemy-veld. In dat geval zijn Field
handig:
import typing
import sqlalchemy as sa
from sqlalchemy.orm import declarative_base
from pydantic import BaseModel, ConfigDict, Field
class MyModel(BaseModel):
model_config = ConfigDict(from_attributes=True)
metadata: typing.Dict[str, str] = Field(alias='metadata_')
Base = declarative_base()
class SQLModel(Base):
__tablename__ = 'my_table'
id = sa.Column('id', sa.Integer, primary_key=True)
# 'metadata' is reserved by SQLAlchemy, hence the '_'
metadata_ = sa.Column('metadata', sa.JSON)
sql_model = SQLModel(metadata_={'key': 'val'}, id=1)
pydantic_model = MyModel.model_validate(sql_model)
print(pydantic_model.model_dump())
#> {'metadata': {'key': 'val'}}
print(pydantic_model.model_dump(by_alias=True))
#> {'metadata_': {'key': 'val'}}
!!! note Het bovenstaande voorbeeld werkt omdat aliassen voorrang hebben op veldnamen bij het vullen van velden. Toegang tot het metadata
van SQLModel
zou leiden tot een ValidationError
.
Geneste attributen¶
Wanneer attributen worden gebruikt om modellen te parseren, worden modelinstanties gemaakt op basis van zowel attributen op het hoogste niveau als dieper geneste attributen, indien van toepassing.
Hier is een voorbeeld dat het principe demonstreert:
from typing import List
from pydantic import BaseModel, ConfigDict
class PetCls:
def __init__(self, *, name: str, species: str):
self.name = name
self.species = species
class PersonCls:
def __init__(self, *, name: str, age: float = None, pets: List[PetCls]):
self.name = name
self.age = age
self.pets = pets
class Pet(BaseModel):
model_config = ConfigDict(from_attributes=True)
name: str
species: str
class Person(BaseModel):
model_config = ConfigDict(from_attributes=True)
name: str
age: float = None
pets: List[Pet]
bones = PetCls(name='Bones', species='dog')
orion = PetCls(name='Orion', species='cat')
anna = PersonCls(name='Anna', age=20, pets=[bones, orion])
anna_model = Person.model_validate(anna)
print(anna_model)
"""
name='Anna' age=20.0 pets=[Pet(name='Bones', species='dog'), Pet(name='Orion', species='cat')]
"""
Foutafhandeling¶
Pydantic zal ValidationError
verhogen wanneer het een fout vindt in de gegevens die het valideert.
Er zal een enkele uitzondering van het type ValidationError
optreden, ongeacht het aantal gevonden fouten, en die ValidationError
zal informatie bevatten over alle fouten en hoe deze zijn gebeurd.
Zie Foutafhandeling voor meer informatie over standaard- en aangepaste fouten.
Als demonstratie:
from typing import List
from pydantic import BaseModel, ValidationError
class Model(BaseModel):
list_of_ints: List[int]
a_float: float
data = dict(
list_of_ints=['1', 2, 'bad'],
a_float='not a float',
)
try:
Model(**data)
except ValidationError as e:
print(e)
"""
2 validation errors for Model
list_of_ints.2
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='bad', input_type=str]
a_float
Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='not a float', input_type=str]
"""
Helperfuncties¶
Pydantic biedt drie classmethod
helperfuncties op modellen voor het parseren van gegevens:
model_validate()
: dit lijkt erg op de__init__
methode van het model, behalve dat er een dictaat of een object voor nodig is in plaats van trefwoordargumenten. Als het doorgegeven object niet kan worden gevalideerd, of als het geen woordenboek of instantie van het betreffende model is, wordt er eenValidationError
gegenereerd.model_validate_json()
: dit neemt een str of bytes en parseert het als json , en geeft het resultaat vervolgens door aanmodel_validate()
.-
model_validate_strings()
: hiervoor is een dictaat nodig (kan worden genest) met stringsleutels en -waarden en valideert de gegevens in json -modus zodat de genoemde strings in de juiste typen kunnen worden gedwongen.from datetime import datetime from typing import Optional
from pydantic import BaseModel, ValidationError
class User(BaseModel): id: int name: str = 'John Doe' signup_ts: Optional[datetime] = None
m = User.model_validate({'id': 123, 'name': 'James'}) print(m)
> id=123 name='James' signup_ts=None¶
try: User.model_validate(['not', 'a', 'dict']) except ValidationError as e: print(e) """ 1 validation error for User Input should be a valid dictionary or instance of User [type=model_type, input_value=['not', 'a', 'dict'], input_type=list] """
m = User.model_validate_json('{"id": 123, "name": "James"}') print(m)
> id=123 name='James' signup_ts=None¶
try: m = User.model_validate_json('{"id": 123, "name": 123}') except ValidationError as e: print(e) """ 1 validation error for User name Input should be a valid string [type=string_type, input_value=123, input_type=int] """
try: m = User.model_validate_json('invalid JSON') except ValidationError as e: print(e) """ 1 validation error for User Invalid JSON: expected value at line 1 column 1 [type=json_invalid, input_value='invalid JSON', input_type=str] """
m = User.model_validate_strings({'id': '123', 'name': 'James'}) print(m)
> id=123 name='James' signup_ts=None¶
m = User.model_validate_strings( {'id': '123', 'name': 'James', 'signup_ts': '2024-04-01T12:00:00'} ) print(m)
> id=123 name='James' signup_ts=datetime.datetime(2024, 4, 1, 12, 0)¶
try: m = User.model_validate_strings( {'id': '123', 'name': 'James', 'signup_ts': '2024-04-01'}, strict=True ) except ValidationError as e: print(e) """ 1 validation error for User signup_ts Input should be a valid datetime, invalid datetime separator, expected
T
,t
,_
or space [type=datetime_parsing, input_value='2024-04-01', input_type=str] """
Als u geserialiseerde gegevens in een ander formaat dan JSON wilt valideren, moet u de gegevens zelf in een dictaat laden en deze vervolgens doorgeven aan model_validate
.
!!! opmerking Afhankelijk van de betrokken typen en modelconfiguraties kunnen model_validate
en model_validate_json
verschillend validatiegedrag hebben. Als u gegevens heeft die afkomstig zijn van een niet-JSON-bron, maar hetzelfde validatiegedrag en dezelfde fouten wilt die u zou krijgen van model_validate_json
, is onze aanbeveling voor nu om een van beide te gebruiken model_validate_json(json.dumps(data))
, of gebruik model_validate_strings
als de gegevens de vorm aannemen van een (potentieel genest) dictaat met stringsleutels en waarden.
!!! note Meer informatie over JSON-parsering vindt u in de JSON -sectie van de documentatie.
!!! opmerking Als u een exemplaar van een model doorgeeft aan model_validate
, kunt u overwegen revalidate_instances
in te stellen in de configuratie van het model. Als u deze waarde niet instelt, wordt de validatie op modelinstanties overgeslagen. Zie het onderstaande voorbeeld:
\=== "
revalidate_instances='never'
" ```py uit pydantic import BaseModel
class Model(BaseModel):
a: int
m = Model(a=0)
# note: the `model_config` setting validate_assignment=True` can prevent this kind of misbehavior
m.a = 'not an int'
# doesn't raise a validation error even though m is invalid
m2 = Model.model_validate(m)
```
\=== "
revalidate_instances='always'
" ```py uit pydantic import BaseModel, ConfigDict, ValidationError
class Model(BaseModel):
a: int
model_config = ConfigDict(revalidate_instances='always')
m = Model(a=0)
# note: the `model_config` setting validate_assignment=True` can prevent this kind of misbehavior
m.a = 'not an int'
try:
m2 = Model.model_validate(m)
except ValidationError as e:
print(e)
"""
1 validation error for Model
a
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='not an int', input_type=str]
"""
```
Modellen maken zonder validatie¶
Pydantic biedt ook de methode model_construct()
, waarmee modellen zonder validatie kunnen worden gemaakt. Dit kan in ten minste een paar gevallen nuttig zijn:
- bij het werken met complexe gegevens waarvan al bekend is dat ze geldig zijn (om prestatieredenen)
- wanneer een of meer van de validatorfuncties niet-idempotent zijn, of
- wanneer een of meer van de validatorfuncties bijwerkingen hebben waarvan u niet wilt dat deze worden geactiveerd.
!!! opmerking In Pydantic V2 is de prestatiekloof tussen BaseModel.__init__
en BaseModel.model_construct
aanzienlijk kleiner geworden. Voor eenvoudige modellen kan het aanroepen van BaseModel.__init__
zelfs sneller zijn. Als u model_construct()
gebruikt om prestatieredenen, wilt u wellicht uw gebruiksscenario profileren voordat u ervan uitgaat dat model_construct()
sneller is.
!!! waarschuwing model_construct()
voert geen enkele validatie uit, wat betekent dat het modellen kan maken die ongeldig zijn. U moet de methode model_construct()
alleen gebruiken met gegevens die al zijn gevalideerd of die u zeker vertrouwt.
from pydantic import BaseModel
class User(BaseModel):
id: int
age: int
name: str = 'John Doe'
original_user = User(id=123, age=32)
user_data = original_user.model_dump()
print(user_data)
#> {'id': 123, 'age': 32, 'name': 'John Doe'}
fields_set = original_user.model_fields_set
print(fields_set)
#> {'age', 'id'}
# ...
# pass user_data and fields_set to RPC or save to the database etc.
# ...
# you can then create a new instance of User without
# re-running validation which would be unnecessary at this point:
new_user = User.model_construct(_fields_set=fields_set, **user_data)
print(repr(new_user))
#> User(id=123, age=32, name='John Doe')
print(new_user.model_fields_set)
#> {'age', 'id'}
# construct can be dangerous, only use it with validated data!:
bad_user = User.model_construct(id='dog')
print(repr(bad_user))
#> User(id='dog', name='John Doe')
Het trefwoordargument _fields_set
voor model_construct()
is optioneel, maar stelt u in staat nauwkeuriger te bepalen welke velden oorspronkelijk waren ingesteld en welke niet. Als dit wordt weggelaten, zijn model_fields_set
slechts de sleutels van de verstrekte gegevens.
Als in het bovenstaande voorbeeld bijvoorbeeld _fields_set
niet is opgegeven, zou new_user.model_fields_set
{'id', 'age', 'name'}
zijn.
Merk op dat voor subklassen van RootModel
de rootwaarde positioneel kan worden doorgegeven aan model_construct()
, in plaats van een trefwoordargument te gebruiken.
Hier zijn enkele aanvullende opmerkingen over het gedrag van model_construct()
:
- Als we zeggen dat er geen validatie is uitgevoerd, omvat dit ook het converteren van dictaten naar modelinstanties. Dus als je een veld hebt met een
Model
moet u het innerlijke dictaat zelf naar een model converteren voordat u het doorgeeft aanmodel_construct()
.- In het bijzonder ondersteunt de methode
model_construct()
geen recursieve constructie van modellen uit dictaten.
- In het bijzonder ondersteunt de methode
- Als u geen trefwoordargumenten doorgeeft voor velden met standaardwaarden, worden de standaardwaarden nog steeds gebruikt.
- Voor modellen met privékenmerken wordt het dictaat
__pydantic_private__
op dezelfde manier geïnitialiseerd als bij het aanroepen van__init__
. - Bij het construeren van een instantie met behulp van
model_construct()
, zal er geen__init__
methode van het model of een van de bovenliggende klassen worden aangeroepen, zelfs niet als een aangepaste__init__
methode is gedefinieerd.
!!! opmerking "Over extra
gedrag met model_construct
" * Voor modellen met model_config['extra'] == 'allow'
, gegevens die niet overeenkomen met velden worden correct opgeslagen in het __pydantic_extra__
dict en opgeslagen in het __dict__
van het model. * Voor modellen met model_config['extra'] == 'ignore'
, gegevens die niet overeenkomen met velden worden genegeerd - dat wil zeggen, niet opgeslagen in __pydantic_extra__
of __dict__
op de instantie. * In tegenstelling tot een aanroep naar __init__
, een aanroep naar model_construct
with model_config['extra'] == 'forbid'
veroorzaakt geen fout in de aanwezigheid van gegevens die niet overeenkomen met velden. In plaats daarvan worden de invoergegevens eenvoudigweg genegeerd.
Generieke modellen¶
Pydantic ondersteunt het creëren van generieke modellen om het hergebruiken van een gemeenschappelijke modelstructuur gemakkelijker te maken.
Om een generiek model te declareren, voert u de volgende stappen uit:
- Declareer een of meer
typing.TypeVar
instanties die u wilt gebruiken om uw model te parametreren. - Declareer een pydantic-model dat overerft van
pydantic.BaseModel
entyping.Generic
, waarbij u deTypeVar
instanties als parameters doorgeeft aantyping.Generic
. - Gebruik de
TypeVar
instanties als annotaties waar u ze wilt vervangen door andere typen of pydantische modellen.
Hier is een voorbeeld waarbij een generieke BaseModel
subklasse wordt gebruikt om een eenvoudig hergebruikte payload-wrapper voor HTTP-antwoorden te maken:
from typing import Generic, List, Optional, TypeVar
from pydantic import BaseModel, ValidationError
DataT = TypeVar('DataT')
class DataModel(BaseModel):
numbers: List[int]
people: List[str]
class Response(BaseModel, Generic[DataT]):
data: Optional[DataT] = None
print(Response[int](data=1))
#> data=1
print(Response[str](data='value'))
#> data='value'
print(Response[str](data='value').model_dump())
#> {'data': 'value'}
data = DataModel(numbers=[1, 2, 3], people=[])
print(Response[DataModel](data=data).model_dump())
#> {'data': {'numbers': [1, 2, 3], 'people': []}}
try:
Response[int](data='value')
except ValidationError as e:
print(e)
"""
1 validation error for Response[int]
data
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='value', input_type=str]
"""
Als u model_config
instelt of gebruik maakt van @field_validator
of andere Pydantic-decorators in uw generieke modeldefinitie, worden deze op dezelfde manier toegepast op geparametriseerde subklassen als bij het erven van een BaseModel
subklasse. Alle methoden die in uw generieke klasse zijn gedefinieerd, worden ook overgenomen.
De generieke programma's van Pydantic kunnen ook goed worden geïntegreerd met typecheckers, zodat u alle typecontroles krijgt die u zou verwachten als u voor elke parametrisatie een afzonderlijk type zou declareren.
!!! opmerking Intern maakt Pydantic subklassen van BaseModel
tijdens runtime wanneer generieke modellen worden geparametriseerd. Deze klassen worden in de cache opgeslagen, dus er zou minimale overhead moeten optreden door het gebruik van generieke modellen.
Om te erven van een generiek model en het feit te behouden dat het generiek is, moet de subklasse ook erven van typing.Generic
:
from typing import Generic, TypeVar
from pydantic import BaseModel
TypeX = TypeVar('TypeX')
class BaseClass(BaseModel, Generic[TypeX]):
X: TypeX
class ChildClass(BaseClass[TypeX], Generic[TypeX]):
# Inherit from Generic[TypeX]
pass
# Replace TypeX by int
print(ChildClass[int](X=1))
#> X=1
U kunt ook een generieke subklasse van een BaseModel
maken die de typeparameters in de superklasse gedeeltelijk of volledig vervangt:
from typing import Generic, TypeVar
from pydantic import BaseModel
TypeX = TypeVar('TypeX')
TypeY = TypeVar('TypeY')
TypeZ = TypeVar('TypeZ')
class BaseClass(BaseModel, Generic[TypeX, TypeY]):
x: TypeX
y: TypeY
class ChildClass(BaseClass[int, TypeY], Generic[TypeY, TypeZ]):
z: TypeZ
# Replace TypeY by str
print(ChildClass[str, int](x='1', y='y', z='3'))
#> x=1 y='y' z=3
Als de naam van de concrete subklassen belangrijk is, kunt u ook de standaardgeneratie van namen overschrijven:
from typing import Any, Generic, Tuple, Type, TypeVar
from pydantic import BaseModel
DataT = TypeVar('DataT')
class Response(BaseModel, Generic[DataT]):
data: DataT
@classmethod
def model_parametrized_name(cls, params: Tuple[Type[Any], ...]) -> str:
return f'{params[0].__name__.title()}Response'
print(repr(Response[int](data=1)))
#> IntResponse(data=1)
print(repr(Response[str](data='a')))
#> StrResponse(data='a')
U kunt geparametriseerde generieke modellen als typen in andere modellen gebruiken:
from typing import Generic, TypeVar
from pydantic import BaseModel
T = TypeVar('T')
class ResponseModel(BaseModel, Generic[T]):
content: T
class Product(BaseModel):
name: str
price: float
class Order(BaseModel):
id: int
product: ResponseModel[Product]
product = Product(name='Apple', price=0.5)
response = ResponseModel[Product](content=product)
order = Order(id=1, product=response)
print(repr(order))
"""
Order(id=1, product=ResponseModel[Product](content=Product(name='Apple', price=0.5)))
"""
!!! tip Wanneer u een geparametriseerd generiek model als type in een ander model gebruikt (zoals product: ResponseModel[Product]
), zorg ervoor dat u het generieke model parametriseert wanneer u de modelinstantie initialiseert (zoals response = ResponseModel[Product](content=product)
). Als u dat niet doet, wordt er een ValidationError
gegenereerd, omdat Pydantic het type van het generieke model niet afleidt op basis van de gegevens die eraan worden doorgegeven.
Door dezelfde TypeVar
in geneste modellen te gebruiken, kunt u typerelaties op verschillende punten in uw model afdwingen:
from typing import Generic, TypeVar
from pydantic import BaseModel, ValidationError
T = TypeVar('T')
class InnerT(BaseModel, Generic[T]):
inner: T
class OuterT(BaseModel, Generic[T]):
outer: T
nested: InnerT[T]
nested = InnerT[int](inner=1)
print(OuterT[int](outer=1, nested=nested))
#> outer=1 nested=InnerT[int](inner=1)
try:
nested = InnerT[str](inner='a')
print(OuterT[int](outer='a', nested=nested))
except ValidationError as e:
print(e)
"""
2 validation errors for OuterT[int]
outer
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
nested
Input should be a valid dictionary or instance of InnerT[int] [type=model_type, input_value=InnerT[str](inner='a'), input_type=InnerT[str]]
"""
Bij gebruik van gebonden typeparameters, en bij het niet gespecificeerd laten van typeparameters, behandelt Pydantic generieke modellen op dezelfde manier als hoe het ingebouwde generieke typen zoals List
en Dict
behandelt:
- Als u geen parameters opgeeft voordat u het generieke model instantieert, worden deze gevalideerd als de grens van de
TypeVar
. - Als de betrokken
TypeVar
's geen grenzen hebben, worden ze behandeld alsAny
.
Ook kunnen, net als List
en Dict
, alle parameters die zijn opgegeven met een TypeVar
later worden vervangen door betontypen:
from typing import Generic, TypeVar
from pydantic import BaseModel, ValidationError
AT = TypeVar('AT')
BT = TypeVar('BT')
class Model(BaseModel, Generic[AT, BT]):
a: AT
b: BT
print(Model(a='a', b='a'))
#> a='a' b='a'
IntT = TypeVar('IntT', bound=int)
typevar_model = Model[int, IntT]
print(typevar_model(a=1, b=1))
#> a=1 b=1
try:
typevar_model(a='a', b='a')
except ValidationError as exc:
print(exc)
"""
2 validation errors for Model[int, TypeVar]
a
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
b
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
"""
concrete_model = typevar_model[int]
print(concrete_model(a=1, b=1))
#> a=1 b=1
!!! waarschuwing Hoewel dit mogelijk geen fout oplevert, raden we ten zeerste af om geparametriseerde generieke geneesmiddelen te gebruiken bij isinstance-controles.
For example, you should not do `isinstance(my_model, MyGenericModel[int])`. However, it is fine to do `isinstance(my_model, MyGenericModel)`. (Note that, for standard generics, it would raise an error to do a subclass check with a parameterized generic.)
If you need to perform isinstance checks against parametrized generics, you can do this by subclassing the parametrized generic class. This looks like `class MyIntModel(MyGenericModel[int]): ...` and `isinstance(my_model, MyIntModel)`.
Als een Pydantic-model wordt gebruikt in een TypeVar
grens en het generieke type nooit wordt geparametriseerd, zal Pydantic de grens gebruiken voor validatie, maar de waarde behandelen als Any
in termen van serialisatie:
from typing import Generic, Optional, TypeVar
from pydantic import BaseModel
class ErrorDetails(BaseModel):
foo: str
ErrorDataT = TypeVar('ErrorDataT', bound=ErrorDetails)
class Error(BaseModel, Generic[ErrorDataT]):
message: str
details: Optional[ErrorDataT]
class MyErrorDetails(ErrorDetails):
bar: str
# serialized as Any
error = Error(
message='We just had an error',
details=MyErrorDetails(foo='var', bar='var2'),
)
assert error.model_dump() == {
'message': 'We just had an error',
'details': {
'foo': 'var',
'bar': 'var2',
},
}
# serialized using the concrete parametrization
# note that `'bar': 'var2'` is missing
error = Error[ErrorDetails](
message='We just had an error',
details=ErrorDetails(foo='var'),
)
assert error.model_dump() == {
'message': 'We just had an error',
'details': {
'foo': 'var',
},
}
Hier is nog een voorbeeld van het bovenstaande gedrag, waarin alle permutaties met betrekking tot gebonden specificatie en generieke typeparametrisatie worden opgesomd:
from typing import Generic
from typing_extensions import TypeVar
from pydantic import BaseModel
TBound = TypeVar('TBound', bound=BaseModel)
TNoBound = TypeVar('TNoBound')
class IntValue(BaseModel):
value: int
class ItemBound(BaseModel, Generic[TBound]):
item: TBound
class ItemNoBound(BaseModel, Generic[TNoBound]):
item: TNoBound
item_bound_inferred = ItemBound(item=IntValue(value=3))
item_bound_explicit = ItemBound[IntValue](item=IntValue(value=3))
item_no_bound_inferred = ItemNoBound(item=IntValue(value=3))
item_no_bound_explicit = ItemNoBound[IntValue](item=IntValue(value=3))
# calling `print(x.model_dump())` on any of the above instances results in the following:
#> {'item': {'value': 3}}
Als u een default=...
(beschikbaar in Python >= 3.13 of via typing-extensions
) of beperkingen ( TypeVar('T', str, int)
; merk op dat u deze vorm van een TypeVar
zelden wilt gebruiken), dan de standaardwaarde of beperkingen worden gebruikt voor zowel validatie als serialisatie als de typevariabele niet is geparametriseerd. U kunt dit gedrag overschrijven met pydantic.SerializeAsAny
:
from typing import Generic, Optional
from typing_extensions import TypeVar
from pydantic import BaseModel, SerializeAsAny
class ErrorDetails(BaseModel):
foo: str
ErrorDataT = TypeVar('ErrorDataT', default=ErrorDetails)
class Error(BaseModel, Generic[ErrorDataT]):
message: str
details: Optional[ErrorDataT]
class MyErrorDetails(ErrorDetails):
bar: str
# serialized using the default's serializer
error = Error(
message='We just had an error',
details=MyErrorDetails(foo='var', bar='var2'),
)
assert error.model_dump() == {
'message': 'We just had an error',
'details': {
'foo': 'var',
},
}
class SerializeAsAnyError(BaseModel, Generic[ErrorDataT]):
message: str
details: Optional[SerializeAsAny[ErrorDataT]]
# serialized as Any
error = SerializeAsAnyError(
message='We just had an error',
details=MyErrorDetails(foo='var', bar='baz'),
)
assert error.model_dump() == {
'message': 'We just had an error',
'details': {
'foo': 'var',
'bar': 'baz',
},
}
!!! note Let op: u kunt in de problemen komen als u een generieke parameter niet parametriseert, terwijl valideren tegen de generieke grens gegevensverlies kan veroorzaken. Zie het onderstaande voorbeeld:
from typing import Generic
from typing_extensions import TypeVar
from pydantic import BaseModel
TItem = TypeVar('TItem', bound='ItemBase')
class ItemBase(BaseModel): ...
class IntItem(ItemBase):
value: int
class ItemHolder(BaseModel, Generic[TItem]):
item: TItem
loaded_data = {'item': {'value': 1}}
print(ItemHolder(**loaded_data).model_dump()) # (1)!
#> {'item': {}}
print(ItemHolder[IntItem](**loaded_data).model_dump()) # (2)!
#> {'item': {'value': 1}}
- Wanneer het generieke niet is geparametriseerd, worden de invoergegevens gevalideerd op basis van de generieke grens. Omdat
ItemBase
geen velden heeft, gaat deitem
verloren. - In dit geval wordt de runtime-type-informatie expliciet verstrekt via de generieke parametrisering, zodat de invoergegevens worden gevalideerd aan de hand van de
IntItem
-klasse en de serialisatie-uitvoer overeenkomt met wat wordt verwacht.
Dynamische modelcreatie¶
??? api "API-documentatie" pydantic.main.create_model
Er zijn enkele gevallen waarin het wenselijk is om een model te maken met behulp van runtime-informatie om de velden te specificeren. Hiervoor biedt Pydantic de create_model
functie waarmee modellen direct kunnen worden gemaakt:
from pydantic import BaseModel, create_model
DynamicFoobarModel = create_model(
'DynamicFoobarModel', foo=(str, ...), bar=(int, 123)
)
class StaticFoobarModel(BaseModel):
foo: str
bar: int = 123
Hier zijn StaticFoobarModel
en DynamicFoobarModel
identiek.
Velden worden gedefinieerd door een van de volgende tupelvormen:
(<type>, <default value>)
(<type>, Field(...))
typing.Annotated[<type>, Field(...)]
Het gebruik van een Field(...)
-aanroep als tweede argument in de tupel (de standaardwaarde) maakt een meer geavanceerde veldconfiguratie mogelijk. De volgende zijn dus analoog:
from pydantic import BaseModel, Field, create_model
DynamicModel = create_model(
'DynamicModel',
foo=(str, Field(..., description='foo description', alias='FOO')),
)
class StaticModel(BaseModel):
foo: str = Field(..., description='foo description', alias='FOO')
De speciale trefwoordargumenten __config__
en __base__
kunnen worden gebruikt om het nieuwe model aan te passen. Hierbij hoort onder meer het uitbreiden van een basismodel met extra velden.
from pydantic import BaseModel, create_model
class FooModel(BaseModel):
foo: str
bar: int = 123
BarModel = create_model(
'BarModel',
apple=(str, 'russet'),
banana=(str, 'yellow'),
__base__=FooModel,
)
print(BarModel)
#> <class '__main__.BarModel'>
print(BarModel.model_fields.keys())
#> dict_keys(['foo', 'bar', 'apple', 'banana'])
U kunt ook validators toevoegen door een dictaat door te geven aan het argument __validators__
.
from pydantic import ValidationError, create_model, field_validator
def username_alphanumeric(cls, v):
assert v.isalnum(), 'must be alphanumeric'
return v
validators = {
'username_validator': field_validator('username')(username_alphanumeric)
}
UserModel = create_model(
'UserModel', username=(str, ...), __validators__=validators
)
user = UserModel(username='scolvin')
print(user)
#> username='scolvin'
try:
UserModel(username='scolvi%n')
except ValidationError as e:
print(e)
"""
1 validation error for UserModel
username
Assertion failed, must be alphanumeric [type=assertion_error, input_value='scolvi%n', input_type=str]
"""
!!! note Een dynamisch gemaakt model selecteren:
- the model must be defined globally
- it must provide `__module__`
RootModel
en aangepaste roottypen¶
??? api "API-documentatie" pydantic.root_model.RootModel
Pydantic-modellen kunnen worden gedefinieerd met een "aangepast roottype" door pydantic.RootModel
te subclassificeren.
Het roottype kan elk type zijn dat door Pydantic wordt ondersteund, en wordt gespecificeerd door de generieke parameter van RootModel
. De rootwaarde kan worden doorgegeven aan het model __init__
of model_validate
via het eerste en enige argument.
Hier is een voorbeeld van hoe dit werkt:
from typing import Dict, List
from pydantic import RootModel
Pets = RootModel[List[str]]
PetsByName = RootModel[Dict[str, str]]
print(Pets(['dog', 'cat']))
#> root=['dog', 'cat']
print(Pets(['dog', 'cat']).model_dump_json())
#> ["dog","cat"]
print(Pets.model_validate(['dog', 'cat']))
#> root=['dog', 'cat']
print(Pets.model_json_schema())
"""
{'items': {'type': 'string'}, 'title': 'RootModel[List[str]]', 'type': 'array'}
"""
print(PetsByName({'Otis': 'dog', 'Milo': 'cat'}))
#> root={'Otis': 'dog', 'Milo': 'cat'}
print(PetsByName({'Otis': 'dog', 'Milo': 'cat'}).model_dump_json())
#> {"Otis":"dog","Milo":"cat"}
print(PetsByName.model_validate({'Otis': 'dog', 'Milo': 'cat'}))
#> root={'Otis': 'dog', 'Milo': 'cat'}
Als u rechtstreeks toegang wilt krijgen tot items in het root
of de items wilt herhalen, kunt u aangepaste functies __iter__
en __getitem__
implementeren, zoals weergegeven in het volgende voorbeeld.
from typing import List
from pydantic import RootModel
class Pets(RootModel):
root: List[str]
def __iter__(self):
return iter(self.root)
def __getitem__(self, item):
return self.root[item]
pets = Pets.model_validate(['dog', 'cat'])
print(pets[0])
#> dog
print([pet for pet in pets])
#> ['dog', 'cat']
U kunt ook rechtstreeks subklassen van het geparametriseerde rootmodel maken:
from typing import List
from pydantic import RootModel
class Pets(RootModel[List[str]]):
def describe(self) -> str:
return f'Pets: {", ".join(self.root)}'
my_pets = Pets.model_validate(['dog', 'cat'])
print(my_pets.describe())
#> Pets: dog, cat
Onveranderlijkheid¶
Modellen kunnen worden geconfigureerd om onveranderlijk te zijn via model_config['frozen'] = True
. Wanneer dit is ingesteld, zullen pogingen om de waarden van instantiekenmerken te wijzigen fouten opleveren. Zie de API-referentie voor meer details.
!!! opmerking Dit gedrag werd bereikt in Pydantic V1 via de configuratie-instelling allow_mutation = False
. Deze configuratievlag is verouderd in Pydantic V2 en is vervangen door frozen
.
!!! waarschuwing In Python wordt onveranderlijkheid niet afgedwongen. Ontwikkelaars hebben de mogelijkheid om objecten die conventioneel als "onveranderlijk" worden beschouwd, te wijzigen als ze daarvoor kiezen.
from pydantic import BaseModel, ConfigDict, ValidationError
class FooBarModel(BaseModel):
model_config = ConfigDict(frozen=True)
a: str
b: dict
foobar = FooBarModel(a='hello', b={'apple': 'pear'})
try:
foobar.a = 'different'
except ValidationError as e:
print(e)
"""
1 validation error for FooBarModel
a
Instance is frozen [type=frozen_instance, input_value='different', input_type=str]
"""
print(foobar.a)
#> hello
print(foobar.b)
#> {'apple': 'pear'}
foobar.b['apple'] = 'grape'
print(foobar.b)
#> {'apple': 'grape'}
Een poging om a
te wijzigen veroorzaakte een fout, en a
blijft ongewijzigd. Het dictaat b
is echter veranderlijk, en de onveranderlijkheid van foobar
verhindert niet dat b
wordt gewijzigd.
Abstracte basisklassen¶
Pydantic-modellen kunnen naast Python's Abstract Base Classes (ABC's) worden gebruikt.
import abc
from pydantic import BaseModel
class FooBarModel(BaseModel, abc.ABC):
a: str
b: int
@abc.abstractmethod
def my_abstract_method(self):
pass
Veldbestelling¶
De veldvolgorde is op de volgende manieren van invloed op modellen:
- de veldvolgorde blijft behouden in het modelschema
- veldvolgorde blijft behouden bij validatiefouten
-
veldvolgorde wordt behouden door
.model_dump()
en.model_dump_json()
enz.from pydantic import BaseModel, ValidationError
class Model(BaseModel): a: int b: int = 2 c: int = 1 d: int = 0 e: float
print(Model.model_fields.keys())
> dict_keys(['a', 'b', 'c', 'd', 'e'])¶
m = Model(e=2, a=1) print(m.model_dump())
>¶
try: Model(a='x', b='x', c='x', d='x', e='x') except ValidationError as err: error_locations = [e['loc'] for e in err.errors()]
print(error_locations)
> [('a',), ('b',), ('c',), ('d',), ('e',)]¶
Verplichte velden¶
Om een veld naar wens te declareren, kunt u het declareren met behulp van een annotatie, of een annotatie in combinatie met een Field
. U kunt ook Ellipsis
/ ...
gebruiken om te benadrukken dat een veld vereist is, vooral als u de Field
gebruikt.
De Field
wordt voornamelijk gebruikt om instellingen zoals alias
of description
voor een attribuut te configureren. De constructor ondersteunt Ellipsis
/ ...
als het enige positionele argument. Dit wordt gebruikt om aan te geven dat het genoemde veld verplicht is, hoewel het de typehint is die deze vereiste afdwingt.
from pydantic import BaseModel, Field
class Model(BaseModel):
a: int
b: int = ...
c: int = Field(..., alias='C')
Hier zijn a
, b
en c
allemaal vereist. Dit gebruik van b: int = ...
werkt echter niet goed met mypy en moet vanaf v1.0 in de meeste gevallen worden vermeden.
!!! opmerking In Pydantic V1 krijgen velden die zijn geannoteerd met Optional
of Any
de impliciete standaardwaarde None
, zelfs als er expliciet geen standaardwaarde is opgegeven. Dit gedrag is veranderd in Pydantic V2 en er zijn geen typeannotaties meer die ertoe leiden dat een veld een impliciete standaardwaarde heeft.
See [the migration guide](../migration.md#required-optional-and-nullable-fields) for more details on changes
to required and nullable fields.
Velden met niet-hashbare standaardwaarden¶
Een veel voorkomende bron van bugs in Python is het gebruik van een veranderbaar object als standaardwaarde voor een functie- of methode-argument, omdat dezelfde instantie uiteindelijk bij elke aanroep opnieuw wordt gebruikt.
De dataclasses
-module geeft in dit geval feitelijk een foutmelding, wat aangeeft dat u het argument default_factory
voor dataclasses.field
moet gebruiken.
Pydantic ondersteunt ook het gebruik van een default_factory
voor niet-hashbare standaardwaarden, maar dit is niet vereist. In het geval dat de standaardwaarde niet hashbaar is, zal Pydantic de standaardwaarde diep kopiëren bij het maken van elk exemplaar van het model:
from typing import Dict, List
from pydantic import BaseModel
class Model(BaseModel):
item_counts: List[Dict[str, int]] = [{}]
m1 = Model()
m1.item_counts[0]['a'] = 1
print(m1.item_counts)
#> [{'a': 1}]
m2 = Model()
print(m2.item_counts)
#> [{}]
Velden met dynamische standaardwaarden¶
Wanneer u een veld declareert met een standaardwaarde, wilt u wellicht dat het dynamisch is (dat wil zeggen verschillend voor elk model). Om dit te doen, wilt u misschien een default_factory
gebruiken.
Hier is een voorbeeld:
from datetime import datetime, timezone
from uuid import UUID, uuid4
from pydantic import BaseModel, Field
def datetime_now() -> datetime:
return datetime.now(timezone.utc)
class Model(BaseModel):
uid: UUID = Field(default_factory=uuid4)
updated: datetime = Field(default_factory=datetime_now)
m1 = Model()
m2 = Model()
assert m1.uid != m2.uid
Meer informatie vindt u in de documentatie van de Field
.
Automatisch uitgesloten kenmerken¶
Klasse var¶
Attributen geannoteerd met typing.ClassVar
worden door Pydantic correct behandeld als klassevariabelen en worden geen velden op modelinstanties:
from typing import ClassVar
from pydantic import BaseModel
class Model(BaseModel):
x: int = 2
y: ClassVar[int] = 1
m = Model()
print(m)
#> x=2
print(Model.y)
#> 1
Kenmerken van privémodellen¶
??? api "API-documentatie" pydantic.fields.PrivateAttr
Attributen waarvan de naam een leidend onderstrepingsteken heeft, worden door Pydantic niet als velden behandeld en zijn niet opgenomen in het modelschema. In plaats daarvan worden deze omgezet in een "privéattribuut" dat niet wordt gevalideerd of zelfs maar wordt ingesteld tijdens aanroepen van __init__
, model_validate
, enz.
!!! opmerking Vanaf Pydantic v2.1.0 ontvangt u een NameError als u de Field
-functie probeert te gebruiken met een privé-attribuut. Omdat privékenmerken niet als velden worden behandeld, kan de functie Field() niet worden toegepast.
Hier is een voorbeeld van gebruik:
from datetime import datetime
from random import randint
from pydantic import BaseModel, PrivateAttr
class TimeAwareModel(BaseModel):
_processed_at: datetime = PrivateAttr(default_factory=datetime.now)
_secret_value: str
def __init__(self, **data):
super().__init__(**data)
# this could also be done with default_factory
self._secret_value = randint(1, 5)
m = TimeAwareModel()
print(m._processed_at)
#> 2032-01-02 03:04:05.000006
print(m._secret_value)
#> 3
Namen van privékenmerken moeten beginnen met een onderstrepingsteken om conflicten met modelvelden te voorkomen. Dunder-namen (zoals __attr__
) worden echter niet ondersteund.
Gegevensconversie¶
Pydantic kan invoergegevens casten om deze te dwingen zich te conformeren aan modelveldtypen, en in sommige gevallen kan dit resulteren in verlies van informatie. Bijvoorbeeld:
from pydantic import BaseModel
class Model(BaseModel):
a: int
b: float
c: str
print(Model(a=3.000, b='2.72', c=b'binary data').model_dump())
#> {'a': 3, 'b': 2.72, 'c': 'binary data'}
Dit is een bewuste beslissing van Pydantic en vaak de meest bruikbare aanpak. Zie hier voor een langere discussie over dit onderwerp.
Niettemin wordt ook strikte typecontrole ondersteund.
Modelhandtekening¶
Bij alle Pydantic-modellen wordt de handtekening gegenereerd op basis van hun velden:
import inspect
from pydantic import BaseModel, Field
class FooModel(BaseModel):
id: int
name: str = None
description: str = 'Foo'
apple: int = Field(alias='pear')
print(inspect.signature(FooModel))
#> (*, id: int, name: str = None, description: str = 'Foo', pear: int) -> None
Een nauwkeurige handtekening is nuttig voor introspectiedoeleinden en voor bibliotheken zoals FastAPI
of hypothesis
.
De gegenereerde handtekening respecteert ook aangepaste __init__
functies:
import inspect
from pydantic import BaseModel
class MyModel(BaseModel):
id: int
info: str = 'Foo'
def __init__(self, id: int = 1, *, bar: str, **data) -> None:
"""My custom init!"""
super().__init__(id=id, bar=bar, **data)
print(inspect.signature(MyModel))
#> (id: int = 1, *, bar: str, info: str = 'Foo') -> None
Om in de handtekening te worden opgenomen, moet de alias of naam van een veld een geldige Python-identificatie zijn. Pydantic zal bij het genereren van de handtekening voorrang geven aan de alias van een veld boven zijn naam, maar kan de veldnaam gebruiken als de alias geen geldige Python-identificatie is.
Als de alias en naam van een veld beide geen geldige ID's zijn (wat mogelijk kan zijn door exotisch gebruik van create_model
), wordt een **data
argument toegevoegd. Bovendien zal het **data
argument altijd aanwezig zijn in de handtekening if model_config['extra'] == 'allow'
.
Structurele patroonafstemming¶
Pydantic ondersteunt structurele patroonmatching voor modellen, zoals geïntroduceerd door PEP 636 in Python 3.10.
from pydantic import BaseModel
class Pet(BaseModel):
name: str
species: str
a = Pet(name='Bones', species='dog')
match a:
# match `species` to 'dog', declare and initialize `dog_name`
case Pet(species='dog', name=dog_name):
print(f'{dog_name} is a dog')
#> Bones is a dog
# default case
case _:
print('No dog matched')
!!! opmerking Het lijkt misschien alsof een match-case-instructie een nieuw model creëert, maar laat u niet voor de gek houden; het is slechts syntactische suiker voor het verkrijgen van een attribuut en het vergelijken ervan of het declareren en initialiseren ervan.
Ken kopieën toe¶
In veel gevallen worden argumenten die aan de constructor worden doorgegeven, gekopieerd om validatie en, waar nodig, dwang uit te voeren.
Houd er in dit voorbeeld rekening mee dat de ID van de lijst verandert nadat de klasse is samengesteld, omdat deze tijdens de validatie is gekopieerd:
from typing import List
from pydantic import BaseModel
class C1:
arr = []
def __init__(self, in_arr):
self.arr = in_arr
class C2(BaseModel):
arr: List[int]
arr_orig = [1, 9, 10, 3]
c1 = C1(arr_orig)
c2 = C2(arr=arr_orig)
print('id(c1.arr) == id(c2.arr):', id(c1.arr) == id(c2.arr))
#> id(c1.arr) == id(c2.arr): False
!!! opmerking Er zijn situaties waarin Pydantic geen attributen kopieert, zoals bij het doorgeven van modellen; we gebruiken het model zoals het is. U kunt dit gedrag overschrijven door in te stellen model_config['revalidate_instances'] = 'always'
.
Extra velden¶
Standaard zullen Pydantic-modellen geen fouten veroorzaken wanneer u gegevens verstrekt voor niet-herkende velden; ze worden gewoon genegeerd:
from pydantic import BaseModel
class Model(BaseModel):
x: int
m = Model(x=1, y='a')
assert m.model_dump() == {'x': 1}
Als u wilt dat dit een fout veroorzaakt, kunt u dit bereiken via model_config
:
from pydantic import BaseModel, ConfigDict, ValidationError
class Model(BaseModel):
x: int
model_config = ConfigDict(extra='forbid')
try:
Model(x=1, y='a')
except ValidationError as exc:
print(exc)
"""
1 validation error for Model
y
Extra inputs are not permitted [type=extra_forbidden, input_value='a', input_type=str]
"""
Om in plaats daarvan extra verstrekte gegevens te behouden, kunt u extra='allow'
instellen. De extra velden worden dan opgeslagen in BaseModel.__pydantic_extra__
:
from pydantic import BaseModel, ConfigDict
class Model(BaseModel):
x: int
model_config = ConfigDict(extra='allow')
m = Model(x=1, y='a')
assert m.__pydantic_extra__ == {'y': 'a'}
Standaard wordt er geen validatie toegepast op deze extra items, maar u kunt een type voor de waarden instellen door de typeannotatie voor __pydantic_extra__
te overschrijven:
from typing import Dict
from pydantic import BaseModel, ConfigDict, Field, ValidationError
class Model(BaseModel):
__pydantic_extra__: Dict[str, int] = Field(init=False) # (1)!
x: int
model_config = ConfigDict(extra='allow')
try:
Model(x=1, y='a')
except ValidationError as exc:
print(exc)
"""
1 validation error for Model
y
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
"""
m = Model(x=1, y='2')
assert m.x == 1
assert m.y == 2
assert m.model_dump() == {'x': 1, 'y': 2}
assert m.__pydantic_extra__ == {'y': 2}
- Het
= Field(init=False)
heeft geen enkel effect tijdens runtime, maar voorkomt dat het veld__pydantic_extra__
door typecheckers wordt behandeld als een argument voor de__init__
methode van het model.
Dezelfde configuraties zijn van toepassing op TypedDict
en dataclass
', behalve dat de configuratie wordt beheerd door het __pydantic_config__
attribuut van de klasse in te stellen op een geldige ConfigDict
.
本文总阅读量次