Pydantic v2 rewrote the core in Rust and renamed a lot of the public API. Most projects hit the same handful of changes first.
The renames
- user.dict() + user.model_dump() - user.json() + user.model_dump_json() - User.parse_obj(data) + User.model_validate(data) - User.parse_raw(text) + User.model_validate_json(text) Validators changed shape, not just name
- from pydantic import validator + from pydantic import field_validator class User(BaseModel): email: str - @validator("email", pre=True) - def clean(cls, v): + @field_validator("email", mode="before") + @classmethod + def clean(cls, v): return v.strip().lower() pre=True is mode="before", and the validator is an explicit classmethod.
Config moved
class User(BaseModel): - class Config: - orm_mode = True + model_config = ConfigDict(from_attributes=True) Note the rename inside it too: orm_mode is from_attributes.
The trap: it mostly still works
v2 keeps most v1 names as deprecated aliases. So after pip install -U pydantic, a lot of code keeps running and prints PydanticDeprecatedSince20 warnings. That's helpful for the upgrade and bad for finishing it: the warnings get filtered out, and the work waits for the release that removes them.
Make them loud in your tests:
# pytest.ini [pytest] filterwarnings = error::pydantic.warnings.PydanticDeprecatedSince20 Let the tool do most of it
pip install bump-pydantic bump-pydantic my_package/ It handles most renames and validator changes. Then run your whole test suite, because behaviour changed in places too (for example, v2 is stricter about some type coercions).
This loop of rewrite, run the tests, compare with before, is what Versyka does for breaking upgrades, and it opens the pull request only if nothing new fails.
If your requirements still pin pydantic<2, what's stopping the upgrade?
