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.dict
with the newModel.model_dump
method -
Replace
Model.doc
with the newModel.model_dump_doc
method -
Replace
Model.parse_doc
with the newModel.model_validate_doc
method -
Replace
Model.update
with the newModel.model_update
method -
Replace
Model.copy
with the newModel.model_copy
method
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.