??? API「APIドキュメント」 pydantic.main.BaseModel
Pydantic でスキーマを定義する主な方法の 1 つはモデルを使用することです。モデルは、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
2 つのフィールドを持つモデルです。
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 の強制ロジックの詳細については、 「データ変換」を参照してください。モデルのフィールドには、 user
オブジェクトの通常の属性としてアクセスできます。文字列'123'
は、フィールドの型に従って int に変換されました。
assert user.name == 'Jane Doe'
user
初期化時にname
が設定されていないため、デフォルト値が使用されます。
assert user.model_fields_set == {'id'}
ユーザーの初期化時に指定されたフィールド。
assert user.model_dump() == {'id': 123, 'name': 'Jane Doe'}
.model_dump()
またはdict(user)
フィールドの dict を提供しますが、 .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()
:model_dump()
の JSON 文字列表現を返します。 「 シリアル化 」を参照してください。model_extra
: 検証中に設定された追加のフィールドを取得します。model_fields_set
: モデル インスタンスの初期化時に設定されたフィールドのセット。model_json_schema()
: モデルを JSON スキーマとして表す jsonable 辞書を返します。 「JSON スキーマ」を参照してください。model_parametrized_name()
: ジェネリック クラスのパラメーター化のクラス名を計算します。model_post_init()
: モデルの初期化後に追加の初期化を実行します。model_rebuild()
: モデル スキーマを再構築します。これは、再帰的な汎用モデルの構築もサポートします。 「モデル スキーマの再構築」を参照してください。model_validate()
: 任意のオブジェクトをモデルにロードするためのユーティリティ。 「ヘルパー関数」を参照してください。model_validate_json()
: 指定された JSON データを Pydantic モデルに対して検証するためのユーティリティ。 「ヘルパー関数」を参照してください。
!!! note メソッドと属性の完全なリストを含むクラス定義については、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()
が V1 のupdate_forward_refs()
を置き換えました。新しい動作には若干の違いがあります。最大の変更点は、最も外側のモデルで model_rebuild()
を呼び出すと、モデル全体 (ネストされたモデルとすべて) の検証に使用されるコア スキーマが構築されるため、すべての型が構築されることです。 model_rebuild()
が呼び出される前にレベルを準備する必要があります。
任意のクラスインスタンス¶
(以前は「ORM モード」/ from_orm
と呼ばれていました。)
Pydantic モデルは、モデルのフィールド名に対応するインスタンス属性を読み取ることで、任意のクラス インスタンスから作成することもできます。この機能の一般的なアプリケーションの 1 つは、オブジェクト リレーショナル マッピング (ORM) との統合です。
これを行うには、config 属性を設定します 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']
"""
予約された名前¶
予約済みの SQLAlchemy フィールドにちなんでColumn
に名前を付けることもできます。その場合、 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 注 フィールド入力ではエイリアスがフィールド名よりも優先されるため、上記の例は機能します。 SQLModel
のmetadata
属性にアクセスすると、 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
タイプの例外が 1 つ発生し、その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 は、_データを解析するためのモデルに対して 3 つのclassmethod
ヘルパー関数を提供します。
model_validate()
: これはモデルの__init__
メソッドと非常によく似ていますが、キーワード引数ではなく辞書またはオブジェクトを取る点が異なります。渡されたオブジェクトが検証できない場合、またはそれが問題のモデルのディクショナリまたはインスタンスではない場合、ValidationError
が発生します。model_validate_json()
: これは_str_または_bytes_を受け取り、それを_json_として解析し、結果をmodel_validate()
に渡します。-
model_validate_strings()
: これは、文字列キーと値を含む dict (ネスト可能) を受け取り、 _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))
、または、データが文字列のキーと値を含む (ネストされている可能性がある) dict の形式をとる場合は、model_validate_strings
を使用します。
!!! note JSON 解析の詳細については、ドキュメントのJSONセクションを参照してください。
!!! note モデルのインスタンスを model_validate
に渡す場合は、モデルの構成でrevalidate_instances
設定することを検討してください。この値を設定しない場合、モデル インスタンスの検証はスキップされます。以下の例を参照してください。
\=== "
revalidate_instances='never'
" pydantic import BaseModel からの ```py
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'
" pydantic import BaseModel、ConfigDict、ValidationError からの ```py
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()
メソッドも提供します。これは少なくともいくつかの場合に役立ちます。
- すでに有効であることがわかっている複雑なデータを操作する場合 (パフォーマンス上の理由から)
- 1 つ以上のバリデーター関数が非冪等である場合、または
- 1 つ以上のバリデーター関数にトリガーされたくない副作用がある場合。
!!! 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')
model_construct()
の_fields_set
キーワード引数はオプションですが、どのフィールドが最初に設定され、どのフィールドが設定されていなかったかをより正確に知ることができます。省略した場合、model_fields_set
は、単に提供されたデータのキーになります。
たとえば、上記の例では、 _fields_set
が指定されていない場合、 new_user.model_fields_set
{'id', 'age', 'name'}
になります。
RootModel
のサブクラスの場合、キーワード引数を使用する代わりに、ルート値を位置的に model_construct()
に渡すことができることに注意してください。
model_construct()
の動作に関する追加の注意事項を次に示します。
- 「検証は実行されない」という場合、これには辞書をモデル インスタンスに変換することも含まれます。したがって、フィールドがある場合は、
Model
型の場合、model_construct()
に渡す前に、内部辞書を自分でモデルに変換する必要があります。- 特に、
model_construct()
メソッドは、辞書からの再帰的なモデルの構築をサポートしていません。
- 特に、
- デフォルト値を持つフィールドにキーワード引数を渡さなかった場合でも、デフォルト値が使用されます。
- プライベート属性を持つモデルの場合、
__pydantic_private__
dict は__init__
呼び出すときと同じように初期化されます。 model_construct()
を使用してインスタンスを構築する場合、カスタム__init__
メソッドが定義されている場合でも、モデルまたはその親クラスの__init__
メソッドは呼び出されません。
!!! note " model_construct
によるextra
動作について" * を備えたモデルの場合 model_config['extra'] == 'allow'
、フィールドに対応しないデータは__pydantic_extra__
dict に正しく格納され、モデルの__dict__
に保存されます。 ※ が付いているモデルの場合 model_config['extra'] == 'ignore'
、フィールドに対応しないデータは無視されます。つまり、インスタンスの__pydantic_extra__
または__dict__
には保存されません。 * __init__
の呼び出しとは異なり、 model_construct
の呼び出しは model_config['extra'] == 'forbid'
フィールドに対応しないデータが存在してもエラーは発生しません。むしろ、前記入力データは単純に無視される。
汎用モデル¶
Pydantic は、共通のモデル構造の再利用を容易にする汎用モデルの作成をサポートしています。
ジェネリック モデルを宣言するには、次の手順を実行します。
- モデルのパラメーター化に使用する 1 つ以上の
typing.TypeVar
インスタンスを宣言します。 pydantic.BaseModel
とtyping.Generic
を継承する pydantic モデルを宣言し、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)
)。そうしないと、Pydantic は渡されたデータに基づいてジェネリック モデルの型を推論しないため、 ValidationError
発生します。
入れ子になったモデルで同じ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
!!!警告 エラーは発生しない可能性がありますが、インスタンス チェックではパラメータ化されたジェネリックスを使用しないことを強くお勧めします。
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(...)
呼び出しをタプルの 2 番目の引数 (デフォルト値) として使用すると、より高度なフィールド構成が可能になります。したがって、次のようなものは類似しています。
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
仕様を組み合わせてフィールドを宣言できます。特にField
コンストラクターを使用する場合は、 Ellipsis
/ ...
使用してフィールドが必須であることを強調することもできます。
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では適切に機能しないため、 v1.0ではほとんどの場合は避けるべきです。
!!! note 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
モジュールはエラーを発生させ、 dataclasses.field
にdefault_factory
引数を使用する必要があることを示します。
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 v2.1.0 以降、プライベート属性を指定してField
関数を使用しようとすると、NameError が返されます。プライベート属性はフィールドとして扱われないため、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
モデルフィールドとの競合を防ぐために、プライベート属性名はアンダースコアで始まる必要があります。ただし、ダンダー名 ( __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 は、Python 3.10 のPEP 636で導入されたモデルの構造パターン マッチングをサポートしています。
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 ステートメントは、あたかも新しいモデルを作成するかのように見えるかもしれませんが、だまされないでください。これは、属性を取得して比較するか、宣言して初期化するための単なる構文糖です。
属性のコピー¶
多くの場合、コンストラクターに渡される引数は、検証と必要に応じて強制を実行するためにコピーされます。
この例では、リストの ID は検証中にコピーされたため、クラスの構築後に変更されることに注意してください。
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
に設定することによって設定が制御される点が異なります。
本文总阅读量次