コンテンツへスキップ

Pydantic v1からPydantic v2への移行

古いFastAPIアプリをお使いの場合、Pydanticバージョン1を使用している可能性があります。

FastAPIは、バージョン0.100.0以降、Pydantic v1またはv2のいずれかをサポートしています。

Pydantic v2をインストールした場合、それを使用します。代わりにPydantic v1をインストールしていた場合は、そちらを使用します。

Pydantic v1は現在非推奨となっており、FastAPIの次のバージョンでサポートが削除される予定ですので、Pydantic v2に移行してください。これにより、最新の機能、改善、修正を得ることができます。

Warning

また、Pydanticチームは、Python 3.14以降の最新バージョンのPythonに対するPydantic v1のサポートを終了しました。

Pythonの最新機能を使用したい場合は、Pydantic v2を使用するようにしてください。

Pydantic v1を使用している古いFastAPIアプリをお持ちの場合、ここではPydantic v2への移行方法と、段階的な移行を支援するためのFastAPI 0.119.0の新機能について説明します。

公式ガイド

Pydanticには、v1からv2への公式の移行ガイドがあります。

変更点、バリデーションがどのように改善され、より正確で厳密になったか、注意点なども含まれています。

変更点をよりよく理解するために、お読みください。

テスト

アプリにテストがあることを確認し、継続的インテグレーション(CI)で実行してください。

これにより、アップグレードを行い、すべてが期待どおりに機能していることを確認できます。

bump-pydantic

多くの場合、カスタマイズなしで通常のPydanticモデルを使用している場合、Pydantic v1からPydantic v2への移行プロセスを自動化できます。

同じPydanticチームのbump-pydanticを使用できます。

このツールは、変更が必要なコードの大部分を自動的に変更するのに役立ちます。

その後、テストを実行してすべてが機能するかどうかを確認できます。問題がなければ完了です。😎

Pydantic v1をv2で使う

Pydantic v2には、Pydantic v1のすべてがサブモジュールpydantic.v1として含まれています。

これは、Pydantic v2の最新バージョンをインストールし、古いPydantic v1コンポーネントをこのサブモジュールからインポートして使用できることを意味します。まるで古いPydantic v1がインストールされているかのように。

from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    size: float
🤓 その他のバージョンとバリアント
from typing import Union

from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    size: float

FastAPIにおけるPydantic v1とv2のサポート

FastAPI 0.119.0以降、Pydantic v2内からのPydantic v1の部分的サポートも提供されており、v2への移行を容易にしています。

そのため、Pydanticを最新のバージョン2にアップグレードし、インポートをpydantic.v1サブモジュールを使用するように変更すると、多くの場合、そのまま動作します。

from fastapi import FastAPI
from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    size: float


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    size: float


app = FastAPI()


@app.post("/items/")
async def create_item(item: Item) -> Item:
    return item

Warning

Pydanticチームは、Python 3.14以降の最新バージョンではPydantic v1のサポートを終了しているため、pydantic.v1の使用もPython 3.14以降ではサポートされないことに注意してください。

同じアプリでPydantic v1とv2を併用する

Pydantic v2のモデルで、そのフィールドがPydantic v1モデルとして定義されている場合、またはその逆の場合、Pydanticではサポートされていません

graph TB
    subgraph "❌ Not Supported"
        direction TB
        subgraph V2["Pydantic v2 Model"]
            V1Field["Pydantic v1 Model"]
        end
        subgraph V1["Pydantic v1 Model"]
            V2Field["Pydantic v2 Model"]
        end
    end

    style V2 fill:#f9fff3
    style V1 fill:#fff6f0
    style V1Field fill:#fff6f0
    style V2Field fill:#f9fff3

しかし、同じアプリでPydantic v1とv2のモデルを分離して使用することは可能です。

graph TB
    subgraph "✅ Supported"
        direction TB
        subgraph V2["Pydantic v2 Model"]
            V2Field["Pydantic v2 Model"]
        end
        subgraph V1["Pydantic v1 Model"]
            V1Field["Pydantic v1 Model"]
        end
    end

    style V2 fill:#f9fff3
    style V1 fill:#fff6f0
    style V1Field fill:#fff6f0
    style V2Field fill:#f9fff3

場合によっては、FastAPIアプリの同じパス操作でPydantic v1とv2の両方のモデルを使用することも可能です。

from fastapi import FastAPI
from pydantic import BaseModel as BaseModelV2
from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    size: float


class ItemV2(BaseModelV2):
    name: str
    description: str | None = None
    size: float


app = FastAPI()


@app.post("/items/", response_model=ItemV2)
async def create_item(item: Item):
    return item
🤓 その他のバージョンとバリアント
from typing import Union

from fastapi import FastAPI
from pydantic import BaseModel as BaseModelV2
from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    size: float


class ItemV2(BaseModelV2):
    name: str
    description: Union[str, None] = None
    size: float


app = FastAPI()


@app.post("/items/", response_model=ItemV2)
async def create_item(item: Item):
    return item

上記の例では、入力モデルはPydantic v1モデルであり、出力モデル(response_model=ItemV2で定義)はPydantic v2モデルです。

Pydantic v1のパラメータ

Pydantic v1モデルでBodyQueryFormなどのFastAPI固有のパラメータツールを使用する必要がある場合は、Pydantic v2への移行が完了するまでfastapi.temp_pydantic_v1_paramsからインポートできます。

from typing import Annotated

from fastapi import FastAPI
from fastapi.temp_pydantic_v1_params import Body
from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    size: float


app = FastAPI()


@app.post("/items/")
async def create_item(item: Annotated[Item, Body(embed=True)]) -> Item:
    return item
🤓 その他のバージョンとバリアント
from typing import Annotated, Union

from fastapi import FastAPI
from fastapi.temp_pydantic_v1_params import Body
from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    size: float


app = FastAPI()


@app.post("/items/")
async def create_item(item: Annotated[Item, Body(embed=True)]) -> Item:
    return item
from typing import Union

from fastapi import FastAPI
from fastapi.temp_pydantic_v1_params import Body
from pydantic.v1 import BaseModel
from typing_extensions import Annotated


class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    size: float


app = FastAPI()


@app.post("/items/")
async def create_item(item: Annotated[Item, Body(embed=True)]) -> Item:
    return item

段階的に移行する

ヒント

まずbump-pydanticを試してください。テストがパスして動作すれば、1コマンドで完了です。✨

bump-pydanticがユースケースに合わない場合は、同じアプリでPydantic v1とv2のモデルをサポートすることで、Pydantic v2への移行を段階的に行うことができます。

まずPydanticを最新のバージョン2にアップグレードし、すべてのモデルのインポートをpydantic.v1を使用するように変更できます。

その後、Pydantic v1からv2へのモデルの移行をグループ化して、段階的に開始できます。🚶