??? API "Документация по API" pydantic.main.BaseModel
Одним из основных способов определения схемы в Pydantic является использование моделей. Модели — это просто классы, которые наследуются от pydantic.BaseModel
и определяют поля как аннотированные атрибуты.
Вы можете думать о моделях как о структурах в таких языках, как C, или как о требованиях к одной конечной точке в API.
Модели имеют много общего с классами данных Python, но были разработаны с некоторыми тонкими, но важными различиями, которые упрощают определенные рабочие процессы, связанные с проверкой, сериализацией и генерацией схемы JSON. Более подробное обсуждение этого вопроса можно найти в разделе документации « Классы данных ».
Ненадежные данные могут быть переданы в модель, и после анализа и проверки Pydantic гарантирует, что поля результирующего экземпляра модели будут соответствовать типам полей, определенным в модели.
!!! примечание «Проверка — намеренное неправильное название» ### 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).
Базовое использование модели¶
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str = 'Jane Doe'
В этом примере User
— это модель с двумя полями:
id
, который является целым числом и является обязательнымname
, которое является строкой и не является обязательным (оно имеет значение по умолчанию).
пользователь = Пользователь (id = '123')
В этом примере user
является экземпляром User
. Инициализация объекта выполнит весь анализ и проверку. Если ValidationError
не возникает, вы знаете, что полученный экземпляр модели действителен.
assert user.id == 123
assert isinstance(user.id, int)
# Note that '123' was coerced to an int and its value is 123
Более подробную информацию о логике приведения pydantic можно найти в Data Conversion . Доступ к полям модели возможен как к обычным атрибутам user
объекта. Строка '123'
была преобразована в целое число в соответствии с типом поля.
assert user.name == 'Jane Doe'
name
не было установлено при инициализации user
, поэтому оно имеет значение по умолчанию.
assert user.model_fields_set == {'id'}
Поля, которые были предоставлены при инициализации пользователя.
assert user.model_dump() == {'id': 123, 'name': 'Jane Doe'}
Либо .model_dump()
либо dict(user)
предоставит набор полей, но .model_dump()
может принимать множество других аргументов. (Обратите внимание, что dict(user)
не будет рекурсивно преобразовывать вложенные модели в dict, а .model_dump()
это сделает.)
user.id = 321
assert user.id == 321
По умолчанию модели являются изменяемыми, и значения полей можно изменить путем назначения атрибутов.
Методы и свойства модели¶
Приведенный выше пример показывает лишь верхушку айсберга того, на что способны модели. Модели обладают следующими методами и атрибутами:
model_computed_fields
: словарь вычисляемых полей этого экземпляра модели.model_construct()
: метод класса для создания моделей без выполнения проверки. См. Создание моделей без проверки .model_copy()
: возвращает копию (по умолчанию неполную копию) модели. См. Сериализация .model_dump()
: возвращает словарь полей и значений модели. См. Сериализация .model_dump_json()
: возвращает строковое представление JSON дляmodel_dump()
. См. Сериализация .model_extra
: получить дополнительные поля, установленные во время проверки.model_fields_set
: набор полей, которые были установлены при инициализации экземпляра модели.model_json_schema()
: возвращает словарь jsonable, представляющий модель в виде схемы JSON. См. схему JSON .model_parametrized_name()
: вычисляет имя класса для параметризации универсальных классов.model_post_init()
: выполнить дополнительную инициализацию после инициализации модели.model_rebuild()
: перестроить схему модели, которая также поддерживает построение рекурсивных универсальных моделей. См. Перестроение схемы модели .model_validate()
: утилита для загрузки любого объекта в модель. См. Вспомогательные функции .model_validate_json()
: утилита для проверки данных JSON на соответствие модели Pydantic. См. Вспомогательные функции .
!!! Примечание. См. BaseModel
для определения класса, включая полный список методов и атрибутов.
!!! подсказка Подробные сведения об изменениях по сравнению с Pydantic V1 см. в разделе «Изменения в pydantic.BaseModel
в Руководстве по миграции .
Вложенные модели¶
Более сложные иерархические структуры данных можно определить, используя сами модели в качестве типов в аннотациях.
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'}],
}
"""
Информацию о моделях, ссылающихся на себя, см. в отложенных аннотациях .
!!! note Примечание. При определении моделей следите за конфликтами имен между именем поля и его типом, ранее определенной моделью или импортированной библиотекой.
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`.
Перестроить схему модели¶
Схему модели можно перестроить с помощью model_rebuild()
. Это полезно для построения рекурсивных универсальных моделей.
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 пытается автоматически определить, когда это необходимо, и выдает ошибку, если это не было сделано, но вы можете захотеть заранее вызвать model_rebuild()
при работе с рекурсивными моделями или обобщенными моделями.
В версии V2 model_rebuild()
заменил update_forward_refs()
из версии V1. Есть некоторые небольшие различия в новом поведении. Самое большое изменение заключается в том, что при вызове model_rebuild()
для самой внешней модели он создает базовую схему, используемую для проверки всей модели (вложенных моделей и всего остального), поэтому все типы вообще уровни должны быть готовы до вызова model_rebuild()
.
Произвольные экземпляры классов¶
(Ранее известный как «Режим ORM»/ from_orm
.)
Модели Pydantic также можно создавать из произвольных экземпляров классов путем чтения атрибутов экземпляра, соответствующих именам полей модели. Одним из распространенных применений этой функциональности является интеграция с объектно-реляционными сопоставлениями (ORM).
Для этого установите атрибут конфигурации model_config['from_attributes'] = True
. Дополнительные сведения см. в разделах Конфигурация модели и ConfigDict.
В примере здесь используется SQLAlchemy , но тот же подход должен работать для любого ORM.
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']
"""
Зарезервированные имена¶
Возможно, вы захотите назвать Column
в честь зарезервированного поля SQLAlchemy. В этом случае будут удобны псевдонимы Field
:
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 Примечание. Приведенный выше пример работает, поскольку псевдонимы имеют приоритет над именами полей при заполнении полей. Доступ к атрибуту metadata
SQLModel
приведет к ValidationError
.
Вложенные атрибуты¶
При использовании атрибутов для анализа моделей экземпляры модели будут создаваться как из атрибутов верхнего уровня, так и из атрибутов с более глубокой вложенностью, в зависимости от ситуации.
Вот пример, демонстрирующий принцип:
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')]
"""
Обработка ошибок¶
Pydantic будет вызывать ValidationError
всякий раз, когда обнаруживает ошибку в проверяемых данных.
Независимо от количества найденных ошибок будет вызвано единственное исключение типа ValidationError
, и это ValidationError
будет содержать информацию обо всех ошибках и о том, как они произошли.
Подробную информацию о стандартных и пользовательских ошибках см. в разделе «Обработка ошибок» .
В качестве демонстрации:
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]
"""
Вспомогательные функции¶
Pydantic предоставляет три вспомогательные функции classmethod
для моделей для анализа данных:
model_validate()
: это очень похоже на метод__init__
модели, за исключением того, что он принимает словарь или объект, а не аргументы ключевого слова. Если переданный объект не может быть проверен или если он не является словарем или экземпляром рассматриваемой модели, будет выданаValidationError
.model_validate_json()
: принимает строку или байты и анализирует их как json , затем передает результат вmodel_validate()
.-
model_validate_strings()
: принимает словарь (может быть вложенным) со строковыми ключами и значениями и проверяет данные в режиме json , чтобы указанные строки можно было привести к правильным типам.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] """
Если вы хотите проверить сериализованные данные в формате, отличном от JSON, вам следует самостоятельно загрузить данные в dict, а затем передать их в model_validate
.
!!! note Примечание В зависимости от используемых типов и конфигураций модели model_validate
и model_validate_json
могут иметь разное поведение при проверке. Если у вас есть данные, поступающие из источника, отличного от JSON, но вы хотите использовать такое же поведение проверки и ошибки, которые вы получили от model_validate_json
, наша рекомендация на данный момент — использовать либо использование model_validate_json(json.dumps(data))
или используйте model_validate_strings
, если данные принимают форму (потенциально вложенного) словаря со строковыми ключами и значениями.
!!! note Дополнительную информацию о синтаксическом анализе JSON можно найти в разделе документации JSON .
!!! Примечание. Если вы передаете экземпляр модели в model_validate
, вам следует рассмотреть возможность установки revalidate_instances
в конфигурации модели. Если вы не установите это значение, проверка экземпляров модели будет пропущена. См. пример ниже:
\=== " revalidate_instances='never'
" ```py из 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 из 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]
"""
```
Создание моделей без проверки¶
Pydantic также предоставляет метод model_construct()
, который позволяет создавать модели без проверки . Это может быть полезно, по крайней мере, в нескольких случаях:
- при работе со сложными данными, о которых уже известно, что они действительны (из соображений производительности)
- когда одна или несколько функций валидатора неидемпотентны, или
- когда одна или несколько функций валидатора имеют побочные эффекты, которые вы не хотите запускать.
!!! note Примечание. В Pydantic V2 разрыв в производительности между BaseModel.__init__
и BaseModel.model_construct
значительно сократился. Для простых моделей вызов BaseModel.__init__
может быть даже быстрее. Если вы используете model_construct()
из соображений производительности, возможно, вам захочется профилировать свой вариант использования, прежде чем предполагать, что model_construct()
работает быстрее.
!!! предупреждение model_construct()
не выполняет никакой проверки, то есть может создавать недопустимые модели. Вам следует использовать метод model_construct()
только с данными, которые уже проверены или которым вы определенно доверяете.
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')
Аргумент ключевого слова _fields_set
для model_construct()
не является обязательным, но позволяет более точно определить, какие поля были изначально установлены, а какие нет. Если он опущен, model_fields_set
будут просто ключами предоставленных данных.
Например, в приведенном выше примере, если _fields_set
не был указан, new_user.model_fields_set
будет {'id', 'age', 'name'}
.
Обратите внимание, что для подклассов RootModel
корневое значение может быть передано в model_construct()
позиционно, вместо использования аргумента ключевого слова.
Вот несколько дополнительных примечаний о поведении model_construct()
:
- Когда мы говорим «проверка не выполняется», это включает в себя преобразование диктовок в экземпляры модели. Итак, если у вас есть поле с типом
Model
вам нужно будет самостоятельно преобразовать внутренний словарь в модель, прежде чем передавать его вmodel_construct()
.- В частности, метод
model_construct()
не поддерживает рекурсивное построение моделей из диктовок.
- В частности, метод
- Если вы не передаете аргументы ключевого слова для полей со значениями по умолчанию, значения по умолчанию все равно будут использоваться.
- Для моделей с частными атрибутами dict
__pydantic_private__
будет инициализирован так же, как и при вызове__init__
. - При создании экземпляра с помощью
model_construct()
метод__init__
из модели или любого из ее родительских классов вызываться не будет, даже если определен собственный метод__init__
.
!!! примечание «О extra
поведении с model_construct
» * Для моделей с model_config['extra'] == 'allow'
, данные, не соответствующие полям, будут правильно сохранены в __pydantic_extra__
dict и сохранены в __dict__
модели. * Для моделей с model_config['extra'] == 'ignore'
, данные, не соответствующие полям, будут игнорироваться, то есть не будут храниться в __pydantic_extra__
или __dict__
экземпляра. * В отличие от вызова __init__
, вызов model_construct
с model_config['extra'] == 'forbid'
не выдает ошибку при наличии данных, не соответствующих полям. Скорее, указанные входные данные просто игнорируются.
Общие модели¶
Pydantic поддерживает создание универсальных моделей, упрощающих повторное использование общей структуры модели.
Чтобы объявить базовую модель, необходимо выполнить следующие шаги:
- Объявите один или несколько экземпляров
typing.TypeVar
, которые будут использоваться для параметризации вашей модели. - Объявите модель pydantic, которая наследуется от
pydantic.BaseModel
иtyping.Generic
, где вы передаете экземплярыTypeVar
в качестве параметров вtyping.Generic
. - Используйте экземпляры
TypeVar
в качестве аннотаций, где вы захотите заменить их другими типами или моделями pydantic.
Вот пример использования универсального подкласса BaseModel
для создания легко повторно используемой оболочки полезных данных HTTP-ответа:
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]
"""
Если вы установите model_config
или используете @field_validator
или другие декораторы Pydantic в определении общей модели, они будут применяться к параметризованным подклассам так же, как при наследовании от подкласса BaseModel
. Любые методы, определенные в вашем универсальном классе, также будут унаследованы.
Обобщенные шаблоны Pydantic также правильно интегрируются с средствами проверки типов, поэтому вы получаете всю проверку типов, которую можно было бы ожидать, если бы вы объявляли отдельный тип для каждой параметризации.
!!! note Примечание Внутренне Pydantic создает подклассы BaseModel
во время выполнения, когда параметризуются универсальные модели. Эти классы кэшируются, поэтому накладные расходы, связанные с использованием обобщенных моделей, должны быть минимальными.
Чтобы наследовать универсальную модель и сохранить тот факт, что она является универсальной, подкласс также должен наследовать от 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
Вы также можете создать общий подкласс BaseModel
, который частично или полностью заменяет параметры типа в суперклассе:
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
Если имя конкретного подкласса важно, вы также можете переопределить генерацию имени по умолчанию:
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')
Вы можете использовать параметризованные универсальные модели в качестве типов в других моделях:
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)))
"""
!!! Совет. При использовании параметризованной универсальной модели в качестве типа в другой модели (например, product: ResponseModel[Product]
), обязательно параметризуйте указанную общую модель при инициализации экземпляра модели (например, response = ResponseModel[Product](content=product)
). Если вы этого не сделаете, возникнет ошибка ValidationError
, поскольку Pydantic не определяет тип универсальной модели на основе переданных в нее данных.
Использование одной и той же TypeVar
во вложенных моделях позволяет вам обеспечить соблюдение отношений типизации в разных точках вашей модели:
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]]
"""
При использовании параметров связанного типа и оставлении параметров типа неуказанными Pydantic обрабатывает универсальные модели аналогично тому, как он обрабатывает встроенные универсальные типы, такие как List
и Dict
:
- Если вы не укажете параметры перед созданием экземпляра универсальной модели, они проверяются как граница
TypeVar
. - Если задействованные
TypeVar
не имеют границ, они обрабатываются какAny
.
Кроме того, как и в случае с List
и Dict
, любые параметры, указанные с помощью TypeVar
позже можно заменить конкретными типами:
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
!!! предупреждение Хотя это может и не вызвать ошибку, мы настоятельно не рекомендуем использовать параметризованные универсальные шаблоны в проверках isinstance.
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)`.
Если модель Pydantic используется в привязке TypeVar
и универсальный тип никогда не параметризуется, тогда Pydantic будет использовать привязку для проверки, но рассматривать значение как Any
с точки зрения сериализации:
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',
},
}
Вот еще один пример описанного выше поведения, перечисляющий все перестановки, касающиеся связанной спецификации и параметризации универсального типа:
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}}
Если вы используете default=...
(доступно в Python >= 3.13 или через typing-extensions
) или ограничения ( TypeVar('T', str, int)
; обратите внимание, что вы редко хотите использовать эту форму TypeVar
), тогда значение или ограничения по умолчанию будут использоваться как для проверки, так и для сериализации, если переменная типа не параметризована. Вы можете переопределить это поведение, используя 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 Обратите внимание, что у вас могут возникнуть некоторые проблемы, если вы не параметризуете универсальный шаблон, поскольку проверка на соответствие привязке универсального шаблона может привести к потере данных. См. пример ниже:
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}}
- Если универсальный параметр не параметризован, входные данные проверяются на соответствие универсальной границе. Поскольку в
ItemBase
нет полей, информация о поляхitem
теряется. - В этом случае информация о типе среды выполнения предоставляется явно через общую параметризацию, поэтому входные данные проверяются на соответствие классу
IntItem
, а выходные данные сериализации соответствуют ожидаемым.
Создание динамической модели¶
??? API "Документация по API" pydantic.main.create_model
В некоторых случаях желательно создать модель, используя информацию времени выполнения для указания полей. Для этого Pydantic предоставляет функцию create_model
, позволяющую создавать модели «на лету»:
from pydantic import BaseModel, create_model
DynamicFoobarModel = create_model(
'DynamicFoobarModel', foo=(str, ...), bar=(int, 123)
)
class StaticFoobarModel(BaseModel):
foo: str
bar: int = 123
Здесь StaticFoobarModel
и DynamicFoobarModel
идентичны.
Поля определяются одной из следующих форм кортежа:
(<type>, <default value>)
(<type>, Field(...))
typing.Annotated[<type>, Field(...)]
Использование вызова Field(...)
в качестве второго аргумента в кортеже (значение по умолчанию) позволяет выполнить более сложную настройку поля. Таким образом, аналогичны:
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')
Специальные аргументы ключевых слов __config__
и __base__
можно использовать для настройки новой модели. Это включает в себя расширение базовой модели дополнительными полями.
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'])
Вы также можете добавить валидаторы, передав словарь в аргумент __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 Чтобы сохранить динамически созданную модель:
- the model must be defined globally
- it must provide `__module__`
RootModel
и пользовательские корневые типы¶
??? API "Документация по API" pydantic.root_model.RootModel
Модели Pydantic можно определить с помощью «пользовательского корневого типа» путем создания подкласса pydantic.RootModel
.
Корневым типом может быть любой тип, поддерживаемый Pydantic, и он указывается универсальным параметром RootModel
. Корневое значение можно передать модели __init__
или model_validate
через первый и единственный аргумент.
Вот пример того, как это работает:
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'}
Если вы хотите напрямую обращаться к элементам в root
поле или перебирать их, вы можете реализовать собственные функции __iter__
и __getitem__
, как показано в следующем примере.
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']
Вы также можете напрямую создавать подклассы параметризованной корневой модели:
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
Искусственная неизменность¶
Модели можно настроить как неизменяемые с помощью model_config['frozen'] = True
. Если этот параметр установлен, попытка изменить значения атрибутов экземпляра приведет к ошибкам. Дополнительные сведения см. в справке по API.
!!! Примечание. Такое поведение было достигнуто в Pydantic V1 с помощью параметра allow_mutation = False
. Этот флаг конфигурации устарел в Pydantic V2 и заменен на frozen
.
!!! предупреждение. В Python неизменяемость не обеспечивается. Разработчики имеют возможность изменять объекты, которые традиционно считаются «неизменяемыми», если они захотят это сделать.
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'}
Попытка изменить a
привела к ошибке, и a
остается неизменной. Однако dict b
является изменчивым, и неизменность foobar
не препятствует изменению b
.
Абстрактные базовые классы¶
Модели Pydantic можно использовать вместе с абстрактными базовыми классами Python (ABC).
import abc
from pydantic import BaseModel
class FooBarModel(BaseModel, abc.ABC):
a: str
b: int
@abc.abstractmethod
def my_abstract_method(self):
pass
Порядок полей¶
Порядок полей влияет на модели следующим образом:
- порядок полей сохраняется в схеме модели
- порядок полей сохраняется при ошибках проверки
-
порядок полей сохраняется с помощью
.model_dump()
и.model_dump_json()
и т.д.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',)]¶
Обязательные поля¶
Чтобы объявить поле необходимым, вы можете объявить его с помощью аннотации или аннотации в сочетании со спецификацией Field
. Вы также можете использовать Ellipsis
/ ...
, чтобы подчеркнуть, что поле является обязательным, особенно при использовании конструктора Field
.
Функция Field
в основном используется для настройки таких параметров, как alias
или description
атрибута. Конструктор поддерживает Ellipsis
/ ...
в качестве единственного позиционного аргумента. Это используется как способ указать, что указанное поле является обязательным, хотя это требование обеспечивается подсказкой типа.
from pydantic import BaseModel, Field
class Model(BaseModel):
a: int
b: int = ...
c: int = Field(..., alias='C')
Здесь необходимы a
, b
и c
. Однако такое использование b: int = ...
не работает должным образом с mypy , и начиная с версии 1.0 его следует избегать в большинстве случаев.
!!! Примечание. В Pydantic V1 полям, помеченным Optional
или Any
будет неявно присвоено значение по умолчанию None
, даже если явно не указано значение по умолчанию. Это поведение изменилось в Pydantic V2, и больше нет аннотаций типов, которые приводили бы к тому, что поле имело бы неявное значение по умолчанию.
See [the migration guide](../migration.md#required-optional-and-nullable-fields) for more details on changes
to required and nullable fields.
Поля с нехешируемыми значениями по умолчанию¶
Распространенным источником ошибок в Python является использование изменяемого объекта в качестве значения по умолчанию для аргумента функции или метода, поскольку один и тот же экземпляр в конечном итоге используется повторно при каждом вызове.
В этом случае модуль dataclasses
фактически выдает ошибку, указывая на то, что вам следует использовать аргумент default_factory
для dataclasses.field
.
Pydantic также поддерживает использование default_factory
для нехэшируемых значений по умолчанию, но это не обязательно. Если значение по умолчанию не является хешируемым, Pydantic будет глубоко копировать значение по умолчанию при создании каждого экземпляра модели:
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)
#> [{}]
Поля с динамическими значениями по умолчанию¶
При объявлении поля со значением по умолчанию вы можете захотеть, чтобы оно было динамическим (т. е. различным для каждой модели). Для этого вы можете использовать default_factory
.
Вот пример:
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
Более подробную информацию вы можете найти в документации функции Field
.
Автоматически исключенные атрибуты¶
Классовые переменные¶
Атрибуты, аннотированные с помощью typing.ClassVar
, правильно обрабатываются Pydantic как переменные класса и не становятся полями в экземплярах модели:
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
Атрибуты частной модели¶
??? API "Документация по API" pydantic.fields.PrivateAttr
Атрибуты, имя которых начинается с подчеркивания, не рассматриваются Pydantic как поля и не включаются в схему модели. Вместо этого они преобразуются в «частный атрибут», который не проверяется и даже не устанавливается во время вызовов __init__
, model_validate
и т. д.
!!! note Примечание. Начиная с Pydantic версии 2.1.0, вы получите ошибку NameError при попытке использовать функцию Field
с частным атрибутом. Поскольку частные атрибуты не рассматриваются как поля, функцию Field() применить невозможно.
Вот пример использования:
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
Имена частных атрибутов должны начинаться с подчеркивания, чтобы избежать конфликтов с полями модели. Однако имена dunder (например, __attr__
) не поддерживаются.
Преобразование данных¶
Pydantic может привести входные данные в соответствие с типами полей модели, и в некоторых случаях это может привести к потере информации. Например:
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'}
Это обдуманное решение Pydantic, и зачастую это наиболее полезный подход. См. здесь более подробное обсуждение этой темы.
Тем не менее, строгая проверка типов также поддерживается.
Подпись модели¶
Все модели Pydantic будут иметь подпись, созданную на основе их полей:
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
Точная подпись полезна для целей самоанализа и таких библиотек, как FastAPI
или hypothesis
.
Сгенерированная подпись также будет учитывать пользовательские функции __init__
:
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
Чтобы быть включенным в подпись, псевдоним или имя поля должно быть допустимым идентификатором Python. Pydantic будет отдавать приоритет псевдониму поля над его именем при создании подписи, но может использовать имя поля, если псевдоним не является допустимым идентификатором Python.
Если псевдоним и имя поля не являются допустимыми идентификаторами (что может быть возможно благодаря экзотическому использованию create_model
), будет добавлен аргумент **data
. Кроме того, аргумент **data
всегда будет присутствовать в подписи, если model_config['extra'] == 'allow'
.
Структурное сопоставление с образцом¶
Pydantic поддерживает сопоставление структурных шаблонов для моделей, как это представлено в PEP 636 в 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')
!!! note Примечание. Может показаться, что оператор match-case создает новую модель, но не дайте себя обмануть; это просто синтаксический сахар для получения атрибута и его сравнения или объявления и инициализации.
Копии атрибутов¶
Во многих случаях аргументы, передаваемые конструктору, копируются для выполнения проверки и, при необходимости, приведения.
Обратите внимание, что в этом примере идентификатор списка изменяется после создания класса, поскольку он был скопирован во время проверки:
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
!!! note Примечание. В некоторых ситуациях Pydantic не копирует атрибуты, например при передаче моделей — мы используем модель как есть. Вы можете переопределить это поведение, установив model_config['revalidate_instances'] = 'always'
.
Дополнительные поля¶
По умолчанию модели Pydantic не выдают ошибок при предоставлении данных для нераспознанных полей, они просто игнорируются:
from pydantic import BaseModel
class Model(BaseModel):
x: int
m = Model(x=1, y='a')
assert m.model_dump() == {'x': 1}
Если вы хотите, чтобы это вызывало ошибку, вы можете добиться этого с помощью 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]
"""
Чтобы вместо этого сохранить любые предоставленные дополнительные данные, вы можете установить extra='allow'
. Дополнительные поля будут сохранены в 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'}
По умолчанию к этим дополнительным элементам проверка не применяется, но вы можете установить тип значений, переопределив аннотацию типа для __pydantic_extra__
:
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}
= Field(init=False)
не оказывает никакого эффекта во время выполнения, но предотвращает обработку поля__pydantic_extra__
в качестве аргумента метода__init__
модели средствами проверки типов.
Те же конфигурации применяются к TypedDict
и dataclass
', за исключением того, что конфигурация контролируется путем установки атрибута __pydantic_config__
класса в допустимый ConfigDict
.
本文总阅读量次