File: migration_guide.md

package info (click to toggle)
python-odmantic 1.0.2-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, trixie
  • size: 1,640 kB
  • sloc: python: 8,547; sh: 37; makefile: 34; xml: 13; javascript: 3
file content (171 lines) | stat: -rw-r--r-- 4,489 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# Migration Guide

## Migrating to v1

Before migrating ODMantic, have a look at the [Pydantic v2 migration guide](https://docs.pydantic.dev/dev/migration/).

### Upgrading to ODMantic v1

```bash
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:**

```python hl_lines="2"
class MyModel(Model):
    my_field: Optional[str]

assert MyModel().my_field is None
```

**Now:**


```python hl_lines="2"
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:**

```python
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:**
```python
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][odmantic.bson.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:**

```python
class IntBSONStr(int):
    @classmethod
    def __bson__(cls, v) -> str:
        return str(v)
```

**Now:**

```python
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`](https://docs.pydantic.dev/latest/api/config/#pydantic.config.ConfigDict.from_attributes){target="_blank"} configuration option.

For example, with a `UserModel` that is used internally and a `ResponseSchema` that
could be exposed through an API:

```python hl_lines="14"
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 new `Model.model_dump` method

- Replace `Model.doc` with the new `Model.model_dump_doc` method

- Replace `Model.parse_doc` with the new `Model.model_validate_doc` method

- Replace `Model.update` with the new `Model.model_update` method

- Replace `Model.copy` with the new `Model.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:

```python hl_lines="5 12"
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](https://github.com/art049/odmantic/issues){target="_blank"}.