コンテンツにスキップ

??? 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

デフォルトでは、モデルは変更可能であり、フィールド値は属性の割り当てを通じて変更できます。

モデルのメソッドとプロパティ

上の例は、モデルができることの氷山の一角を示しているにすぎません。モデルには次のメソッドと属性があります。

!!! 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 注 フィールド入力ではエイリアスがフィールド名よりも優先されるため、上記の例は機能します。 SQLModelmetadata属性にアクセスすると、 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_validatemodel_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. モデルのパラメーター化に使用する 1 つ以上のtyping.TypeVarインスタンスを宣言します。
  2. pydantic.BaseModeltyping.Genericを継承する pydantic モデルを宣言し、 TypeVarインスタンスをパラメーターとしてtyping.Genericに渡します。
  3. 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 はジェネリック モデルをListDictのような組み込みのジェネリック型を処理する方法と同様に処理します。

  • ジェネリック モデルをインスタンス化する前にパラメーターを指定しない場合、パラメーターはTypeVarの境界として検証されます。
  • 関係するTypeVarに境界がない場合、それらはAnyとして扱われます。

また、 ListDictと同様に、 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}}
  1. ジェネリックがパラメーター化されていない場合、入力データはジェネリックの境界に対して検証されます。 ItemBaseにフィールドがない場合、 itemフィールド情報は失われます。
  2. この場合、ランタイム型情報はジェネリック パラメーター化を通じて明示的に提供されるため、入力データは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

ここで、 StaticFoobarModelDynamicFoobarModelは同一です。

フィールドは、次のタプル形式のいずれかで定義されます。

  • (<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関数は主に、属性のaliasdescriptionなどの設定を構成するために使用されます。コンストラクターは、唯一の位置引数としてEllipsis / ...をサポートします。これは、そのフィールドが必須であることを示す方法として使用されますが、この要件を強制するのはタイプ ヒントです。

from pydantic import BaseModel, Field


class Model(BaseModel):
    a: int
    b: int = ...
    c: int = Field(..., alias='C')

ここで、 abcすべて必須です。ただし、この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.fielddefault_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

正確な署名は、イントロスペクションの目的や、 FastAPIhypothesisなどのライブラリに役立ちます。

生成された署名は、カスタム__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}
  1. = Field(init=False)実行時には何の効果もありませんが、 __pydantic_extra__フィールドが型チェッカーによってモデルの__init__メソッドへの引数として扱われるのを防ぎます。

同じ設定がTypedDictdataclassに適用されます。ただし、クラスの__pydantic_config__属性を有効なConfigDictに設定することによって設定が制御される点が異なります。


本文总阅读量