跳到内容

从 Pydantic v1 迁移到 Pydantic v2

如果您有一个旧的 FastAPI 应用程序,您可能正在使用 Pydantic 版本 1。

从 0.100.0 版本开始,FastAPI 就支持 Pydantic v1 或 v2。

如果您安装了 Pydantic v2,它将使用 v2。如果您安装了 Pydantic v1,它将使用 v1。

Pydantic v1 现已弃用,对其的支持将在 FastAPI 的后续版本中移除,您应该迁移到 Pydantic v2。这样您将获得最新的功能、改进和修复。

警告

此外,Pydantic 团队已停止支持最新 Python 版本中的 Pydantic v1,从 Python 3.14 开始。

如果您想使用 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 v2 中的 Pydantic v1

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 v2 中 Pydantic v1 的支持

从 FastAPI 0.119.0 开始,FastAPI 也部分支持 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

警告

请记住,由于 Pydantic 团队不再支持最新 Python 版本中的 Pydantic v1,从 Python 3.14 开始,使用 pydantic.v1 在 Python 3.14 及更高版本中也不受支持。

同一应用中的 Pydantic v1 和 v2

Pydantic 不支持将 Pydantic v2 模型与作为 Pydantic v1 模型定义的字段混合使用,反之亦然。

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 参数

如果您需要将一些 FastAPI 特定的参数工具(如 BodyQueryForm 等)与 Pydantic v1 模型一起使用,您可以在完成迁移到 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,如果您的测试通过并且它有效,那么您只需一条命令即可完成。✨

如果 bump-pydantic 不适用于您的用例,您可以使用同一应用程序中对 Pydantic v1 和 v2 模型的支持,逐步迁移到 Pydantic v2。

您可以首先将 Pydantic 升级到最新的 2 版本,并将所有模型的导入更改为使用 pydantic.v1

然后,您可以分批、逐步地将模型从 Pydantic v1 迁移到 v2。🚶