Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
ac47072
♻️ Refactor _compat, prepare compatibility for Pydantic v1 in v2
tiangolo Oct 4, 2025
22a1cca
♻️ Refactor internals to use new _compat utils
tiangolo Oct 4, 2025
f6b2f22
♻️ Duplicate params for v1 compatibility
tiangolo Oct 5, 2025
04ed076
♻️ Update and refactor main module in _compat
tiangolo Oct 5, 2025
a83cd00
✨ Add util to check if annotation is Pydantic v1
tiangolo Oct 5, 2025
6c4ec85
♻️ Refactor _compat.v1 module
tiangolo Oct 5, 2025
7cd3a71
♻️ Refactor internals to use new compat utils for v1
tiangolo Oct 5, 2025
cb28e5d
✅ Update tests for compat
tiangolo Oct 5, 2025
0b2faed
✅ Add first tests for Pydantic v1 in v2
tiangolo Oct 5, 2025
bd660ea
✅ Extend test for Pydantic v1 with submodels
tiangolo Oct 5, 2025
fc5932e
✅ Update tests and URL paths
tiangolo Oct 5, 2025
db96db4
✅ Add tests for Pydantic v1 lists
tiangolo Oct 5, 2025
0d4d7d2
♻️ Tweak _compat to account for lists of models
tiangolo Oct 5, 2025
7960130
✅ Update tests for mixing Pydantic v1 and v2 models
tiangolo Oct 5, 2025
f9b5c48
✅ Add tests for Noneable fields
tiangolo Oct 5, 2025
4a60fa8
🐛 Fix import in _compat
tiangolo Oct 5, 2025
a63e1a5
🐛 Handle creating ModelField in v1 when v2 is not installed
tiangolo Oct 5, 2025
c0863b2
✅ Fix test using _compat
tiangolo Oct 5, 2025
f5998c5
✅ Add snapshots for tests in Pydantic v1
tiangolo Oct 5, 2025
ab242c6
✅ Add snapshots for Pydantic v2 tests
tiangolo Oct 5, 2025
aa58482
♻️ Tweak implementation and tests for coverage
tiangolo Oct 5, 2025
9030eed
✅ Add tests for models with multiple paths
tiangolo Oct 5, 2025
e1eedaf
✅ Update tests, separate schemas for models with the same name in dif…
tiangolo Oct 5, 2025
658c260
♻️ Add Pydantic v1 schema functions to v2 _compat to allow generating…
tiangolo Oct 5, 2025
803ca5f
✅ Update snapshots for Pydantic v1 and v2
tiangolo Oct 5, 2025
097dfc0
♻️ Tweak types
tiangolo Oct 8, 2025
09bba35
♻️ Update getting model types from annotations
tiangolo Oct 8, 2025
0a21282
✅ Remove unnecessary test
tiangolo Oct 8, 2025
fa521cf
🔄 Merge branch 'master' into pv2-v1
tiangolo Oct 8, 2025
3667b6a
🔀 Merge new changes from master in the right files
tiangolo Oct 8, 2025
1a9e9a1
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] Oct 8, 2025
49492b0
🎨 Tweak types for Python 3.9 and below
tiangolo Oct 8, 2025
adf9b89
♻️ Workaround for Python 3.8
tiangolo Oct 8, 2025
0f9902a
🎨 Tweak types for Python 3.8
tiangolo Oct 8, 2025
3d14dda
🎨 Tweak types
tiangolo Oct 8, 2025
9e652f3
⚡️ Format and add types
tiangolo Oct 8, 2025
8f0c4db
🎨 Format and fix types
tiangolo Oct 8, 2025
ddad019
✅ Add tests for Pydantic v1 parameters
tiangolo Oct 9, 2025
36e6f22
♻️ Update v1 params to re-use main ParamTypes
tiangolo Oct 9, 2025
b818096
♻️ Update internal checks for types to include Pydantic v1
tiangolo Oct 9, 2025
901c751
✅ Add extra tests for Pydantic v1
tiangolo Oct 9, 2025
0789a87
♻️ Tweak implementation logic for Pydantic v1 compatibility
tiangolo Oct 9, 2025
7ce1e05
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] Oct 9, 2025
aaad3c8
✅ Tweak tests for OpenAPI schema, for compatibility with Pydantic v1 …
tiangolo Oct 9, 2025
4400390
🎨 Tweak imports for Python 3.8
tiangolo Oct 9, 2025
79e0375
🎨 Tweak types
tiangolo Oct 9, 2025
d25fb50
🎨 Tweak types
tiangolo Oct 9, 2025
9a0dabc
✅ Add tests for duplicate models
tiangolo Oct 9, 2025
5aa9d0e
🔥 Remove obsolete TODO comment
tiangolo Oct 9, 2025
25b51f2
✅ Refactor test for compatibility with Pydantic v1 and v2
tiangolo Oct 9, 2025
fda1c44
✅ Update tests
tiangolo Oct 10, 2025
6392ff2
Merge branch 'master' into pv2-v1
tiangolo Oct 10, 2025
f863d90
✅ Tweak tests for compatibility with Pydantic v1 and v2
tiangolo Oct 10, 2025
17377b7
✅ Update tests with skip flag for Pydantic v1 in Python 3.14
tiangolo Oct 10, 2025
1e32fe0
✅ Run Pydantic v1 tests only on Python < 3.14
tiangolo Oct 10, 2025
f2e5568
✅ Update tests for Pydantic v1 and v2, and Python 3.14
tiangolo Oct 10, 2025
11b4558
🔥 Remove unreachable code
tiangolo Oct 10, 2025
6792694
📝 Add source for examples for migration from Pydantic v1 to Pydantic v2
tiangolo Oct 10, 2025
26339b6
📝 Add docs for migrating from Pydantic v1 to v2
tiangolo Oct 10, 2025
367d804
♻️ Refactor Pydantic v1 FastAPI params
tiangolo Oct 10, 2025
f547473
✅ Update tests with new location
tiangolo Oct 10, 2025
9068551
✅ Add tests for examples for migrating from Pydantic v1 to v2
tiangolo Oct 10, 2025
121ed23
🐛 Fix typing import
tiangolo Oct 11, 2025
a0320fb
📝 Update docs
tiangolo Oct 11, 2025
05803e9
📝 Add diagrams to explain Pydantic v1 and v2 models in the same app
tiangolo Oct 11, 2025
49d1911
🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
pre-commit-ci[bot] Oct 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions docs/en/docs/how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Migrate from Pydantic v1 to Pydantic v2 { #migrate-from-pydantic-v1-to-pydantic-v2 }

If you have an old FastAPI app, you might be using Pydantic version 1.

FastAPI has had support for either Pydantic v1 or v2 since version 0.100.0.

If you had installed Pydantic v2, it would use it. If instead you had Pydantic v1, it would use that.

Pydantic v1 is now deprecated and support for it will be removed in the next versions of FastAPI, you should **migrate to Pydantic v2**. This way you will get the latest features, improvements, and fixes.

/// warning

Also, the Pydantic team stopped support for Pydantic v1 for the latest versions of Python, starting with **Python 3.14**.

If you want to use the latest features of Python, you will need to make sure you use Pydantic v2.

///

If you have an old FastAPI app with Pydantic v1, here I'll show you how to migrate it to Pydantic v2, and the **new features in FastAPI 0.119.0** to help you with a gradual migration.

## Official Guide { #official-guide }

Pydantic has an official <a href="https://docs.pydantic.dev/latest/migration/" class="external-link" target="_blank">Migration Guide</a> from v1 to v2.

It also includes what has changed, how validations are now more correct and strict, possible caveats, etc.

You can read it to understand better what has changed.

## Tests { #tests }

Make sure you have [tests](../tutorial/testing.md){.internal-link target=_blank} for your app and you run them on continuous integration (CI).

This way, you can do the upgrade and make sure everything is still working as expected.

## `bump-pydantic` { #bump-pydantic }

In many cases, when you use regular Pydantic models without customizations, you will be able to automate most of the process of migrating from Pydantic v1 to Pydantic v2.

You can use <a href="https://github.com/pydantic/bump-pydantic" class="external-link" target="_blank">`bump-pydantic`</a> from the same Pydantic team.

This tool will help you to automatically change most of the code that needs to be changed.

After this, you can run the tests and check if everything works. If it does, you are done. 😎

## Pydantic v1 in v2 { #pydantic-v1-in-v2 }

Pydantic v2 includes everything from Pydantic v1 as a submodule `pydantic.v1`.

This means that you can install the latest version of Pydantic v2 and import and use the old Pydantic v1 components from this submodule, as if you had the old Pydantic v1 installed.

{* ../../docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py hl[1,4] *}

### FastAPI support for Pydantic v1 in v2 { #fastapi-support-for-pydantic-v1-in-v2 }

Since FastAPI 0.119.0, there's also partial support for Pydantic v1 from inside of Pydantic v2, to facilitate the migration to v2.

So, you could upgrade Pydantic to the latest version 2, and change the imports to use the `pydantic.v1` submodule, and in many cases it would just work.

{* ../../docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py hl[2,5,15] *}

/// warning

Have in mind that as the Pydantic team no longer supports Pydantic v1 in recent versions of Python, starting from Python 3.14, using `pydantic.v1` is also not supported in Python 3.14 and above.

///

### Pydantic v1 and v2 on the same app { #pydantic-v1-and-v2-on-the-same-app }

It's **not supported** by Pydantic to have a model of Pydantic v2 with its own fields defined as Pydantic v1 models or vice versa.

```mermaid
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
```

...but, you can have separated models using Pydantic v1 and v2 in the same app.

```mermaid
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
```

In some cases, it's even possible to have both Pydantic v1 and v2 models in the same **path operation** in your FastAPI app:

{* ../../docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py hl[2:3,6,12,21:22] *}

In this example above, the input model is a Pydantic v1 model, and the output model (defined in `response_model=ItemV2`) is a Pydantic v2 model.

### Pydantic v1 parameters { #pydantic-v1-parameters }

If you need to use some of the FastAPI-specific tools for parameters like `Body`, `Query`, `Form`, etc. with Pydantic v1 models, you can import them from `fastapi.temp_pydantic_v1_params` while you finish the migration to Pydantic v2:

{* ../../docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py hl[4,18] *}

### Migrate in steps { #migrate-in-steps }

/// tip

First try with `bump-pydantic`, if your tests pass and that works, then you're done in one command. ✨

///

If `bump-pydantic` doesn't work for your use case, you can use the support for both Pydantic v1 and v2 models in the same app to do the migration to Pydantic v2 gradually.

You could fist upgrade Pydantic to use the latest version 2, and change the imports to use `pydantic.v1` for all your models.

Then, you can start migrating your models from Pydantic v1 to v2 in groups, in gradual steps. 🚶
1 change: 1 addition & 0 deletions docs/en/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ nav:
- How To - Recipes:
- how-to/index.md
- how-to/general.md
- how-to/migrate-from-pydantic-v1-to-pydantic-v2.md
- how-to/graphql.md
- how-to/custom-request-and-route.md
- how-to/conditional-openapi.md
Expand Down
9 changes: 9 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial001_an.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from typing import Union

from pydantic.v1 import BaseModel


class Item(BaseModel):
name: str
description: Union[str, None] = None
size: float
7 changes: 7 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from pydantic.v1 import BaseModel


class Item(BaseModel):
name: str
description: str | None = None
size: float
18 changes: 18 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial002_an.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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
16 changes: 16 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
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
25 changes: 25 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial003_an.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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
23 changes: 23 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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
20 changes: 20 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial004_an.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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
19 changes: 19 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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
19 changes: 19 additions & 0 deletions docs_src/pydantic_v1_in_v2/tutorial004_an_py39.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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
Loading