Ga naar inhoud

??? 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 is
  • name , 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:

!!! 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 een ValidationError gegenereerd.
  • model_validate_json(): dit neemt een str of bytes en parseert het als json , en geeft het resultaat vervolgens door aan model_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 aan model_construct().
    • In het bijzonder ondersteunt de methode model_construct() geen recursieve constructie van modellen uit dictaten.
  • 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:

  1. Declareer een of meer typing.TypeVar instanties die u wilt gebruiken om uw model te parametreren.
  2. Declareer een pydantic-model dat overerft van pydantic.BaseModel en typing.Generic , waarbij u de TypeVar instanties als parameters doorgeeft aan typing.Generic .
  3. 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 als Any .

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}}
  1. Wanneer het generieke niet is geparametriseerd, worden de invoergegevens gevalideerd op basis van de generieke grens. Omdat ItemBase geen velden heeft, gaat de item verloren.
  2. 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}
  1. 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 .


本文总阅读量