Перейти к содержанию

Миграция с Pydantic v1 на Pydantic v2

Если у вас старое приложение FastAPI, возможно, вы используете Pydantic версии 1.

FastAPI версии 0.100.0 поддерживал либо Pydantic v1, либо v2. Он использовал ту версию, которая была установлена.

FastAPI версии 0.119.0 добавил частичную поддержку Pydantic v1 изнутри Pydantic v2 (как pydantic.v1), чтобы упростить миграцию на v2.

FastAPI 0.126.0 убрал поддержку Pydantic v1, при этом ещё некоторое время продолжал поддерживать pydantic.v1.

Предупреждение

Команда Pydantic прекратила поддержку Pydantic v1 для последних версий Python, начиная с Python 3.14.

Это включает pydantic.v1, который больше не поддерживается в Python 3.14 и выше.

Если вы хотите использовать последние возможности Python, вам нужно убедиться, что вы используете Pydantic v2.

Если у вас старое приложение FastAPI с Pydantic v1, здесь я покажу, как мигрировать на Pydantic v2, и возможности FastAPI 0.119.0, которые помогут выполнить постепенную миграцию.

Официальное руководство

У Pydantic есть официальное руководство по миграции с v1 на v2.

Там также описано, что изменилось, как валидации стали более корректными и строгими, возможные нюансы и т.д.

Прочитайте его, чтобы лучше понять, что изменилось.

Тесты

Убедитесь, что у вас есть тесты для вашего приложения и что вы запускаете их в системе непрерывной интеграции (CI).

Так вы сможете выполнить обновление и убедиться, что всё работает как ожидается.

bump-pydantic

Во многих случаях, когда вы используете обычные Pydantic‑модели без пользовательских настроек, вы сможете автоматизировать большую часть процесса миграции с Pydantic v1 на Pydantic v2.

Вы можете использовать bump-pydantic от той же команды Pydantic.

Этот инструмент поможет автоматически изменить большую часть кода, который нужно изменить.

После этого вы можете запустить тесты и проверить, что всё работает. Если да — на этом всё. 😎

Pydantic v1 в v2

Pydantic v2 включает всё из Pydantic v1 как подмодуль pydantic.v1. Но это больше не поддерживается в версиях Python выше 3.13.

Это означает, что вы можете установить последнюю версию Pydantic v2 и импортировать и использовать старые компоненты Pydantic v1 из этого подмодуля так, как если бы у вас был установлен старый Pydantic v1.

from pydantic.v1 import BaseModel


class Item(BaseModel):
    name: str
    description: str | None = None
    size: float
🤓 Other versions and variants
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 v1 изнутри Pydantic v2, чтобы упростить миграцию на 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
🤓 Other versions and variants
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 больше не поддерживает Pydantic v1 в последних версиях Python, начиная с 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

В некоторых случаях можно использовать и модели Pydantic v1, и v2 в одной и той же операции пути (обработчике пути) вашего приложения FastAPI:

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
🤓 Other versions and variants
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 инструменты для параметров, такие как Body, Query, Form и т.п., с моделями Pydantic v1, вы можете импортировать их из fastapi.temp_pydantic_v1_params, пока завершаете миграцию на Pydantic v2:

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
🤓 Other versions and variants
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

Мигрируйте по шагам

Совет

Сначала попробуйте bump-pydantic: если тесты проходят и всё работает, вы справились одной командой. ✨

Если bump-pydantic не подходит для вашего случая, вы можете использовать поддержку одновременной работы моделей Pydantic v1 и v2 в одном приложении, чтобы мигрировать на Pydantic v2 постепенно.

Сначала вы можете обновить Pydantic до последней 2-й версии и изменить импорты так, чтобы все ваши модели использовали pydantic.v1.

Затем вы можете начать мигрировать ваши модели с Pydantic v1 на v2 группами, поэтапно. 🚶