Migration Guide¶
Migrating to v1¶
Before migrating ODMantic, have a look at the Pydantic v2 migration guide.
Upgrading to ODMantic v1¶
pip install -U pydantic
Handling Optional with non-implicit default None values¶
Since this new version, the default value of an Optional field is not implicit anymore.
Thus, if you want to keep the same behavior, you have to add the default parameter to your Optional fields.
Before:
class MyModel(Model):
my_field: Optional[str]
assert MyModel().my_field is None
Now:
class MyModel(Model):
my_field: Optional[str] = None
assert MyModel().my_field is None
Upgrading models configuration¶
Instead of the old Config class, you have to use the new model_config typed dict.
Before:
class Event(Model):
date: datetime
class Config:
collection = "event_collection"
parse_doc_with_default_factories = True
indexes = [
Index(Event.date, unique=True),
pymongo.IndexModel([("date", pymongo.DESCENDING)]),
]
Now:
class Event(Model):
date: datetime
model_config = {
"collection": "event_collection",
"parse_doc_with_default_factories": True,
"indexes": lambda: [
Index(Event.date, unique=True),
pymongo.IndexModel([("date", pymongo.DESCENDING)]),
],
}
Defining custom BSON serializers¶
Instead of using the __bson__ class method, you have to use the new WithBsonSerializer annotation.
Note
We will probably bring back the __bson__ class method in a future version but
using the new annotation is the recommended way to define custom BSON serializers.
Here is an example of serializing an integer as a string in BSON:
Before:
class IntBSONStr(int):
@classmethod
def __bson__(cls, v) -> str:
return str(v)
Now:
from typing import Annotated
from odmantic import WithBsonSerializer
IntBSONStr = Annotated[int, WithBsonSerializer(lambda v: str(v))]
Building a Pydantic model from an ODMantic model¶
If you want to build a Pydantic model from an ODMantic model, you now have to enable the
from_attributes configuration option.
For example, with a UserModel that is used internally and a ResponseSchema that
could be exposed through an API:
from pydantic import BaseModel, EmailStr
from odmantic import Model
class UserModel(Model):
email: EmailStr
password_hash: str
class UserSchema(BaseModel):
email: EmailStr
class ResponseSchema(BaseModel):
user: UserSchema
model_config = {"from_attributes": True}
user = UserModel(email="john@doe.com", password_hash="...")
response = ResponseSchema(user=user)
Replacing the Model and EmbeddedModel deprecated methods¶
-
Replace
Model.dictwith the newModel.model_dumpmethod -
Replace
Model.docwith the newModel.model_dump_docmethod -
Replace
Model.parse_docwith the newModel.model_validate_docmethod -
Replace
Model.updatewith the newModel.model_updatemethod -
Replace
Model.copywith the newModel.model_copymethod
Custom JSON encoders on odmantic.bson types¶
Custom JSON encoders (defined with the json_encoders config option) are no longer
effective on odmantic.bson types since the builtin encoders cannot be overridden in
that way anymore.
The solution is to use the PlainSerializer annotation provided by Pydantic. For example,
if we want to serialize ObjectId as a id_ prefixed string:
from typing import Annotated
from pydantic import BaseModel, PlainSerializer
from odmantic import ObjectId
MyObjectId = Annotated[ObjectId, PlainSerializer(lambda v: "id_" + str(v))]
class MyModel(BaseModel):
id: MyObjectId
instance = MyModel(id=ObjectId("ffffffffffffffffffffffff"))
print(instance.model_dump_json())
#> {"id": "id_ffffffffffffffffffffffff"}
And ... that's it, congrats! 🚀⚒️
If you have any questions or if you need help to migrate something that is not covered by this guide, feel free to open an issue on GitHub.