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
|
# State Management
Beanie can keep the document state synced with the database in order to find local changes and save only them.
This feature must be explicitly turned on in the `Settings` inner class:
```python
class Sample(Document):
num: int
name: str
class Settings:
use_state_management = True
```
Beanie keeps the current changes (not yet saved in the database) by default (with `use_state_management = True`), AND the previous changes (saved to the database) with `state_management_save_previous = True`.
```python
class Sample(Document):
num: int
name: str
class Settings:
use_state_management = True
state_management_save_previous = True
```
Every new save override the previous changes and clears the current changes.
## Saving changes
To save only changed values, the `save_changes()` method should be used.
```python
s = await Sample.find_one(Sample.name == "Test")
s.num = 100
await s.save_changes()
```
The `save_changes()` method can only be used with already inserted documents.
## Interacting with changes
Beanie exposes several methods that can be used to interact with the saved changes:
```python
s = await Sample.find_one(Sample.name == "Test")
s.is_changed == False
s.get_changes == {}
s.num = 200
s.is_changed == True
s.get_changes() == {"num": 200}
s.rollback()
s.is_changed == False
s.get_changes() == {}
```
And similar methods can be used with the previous changes that have been saved in the database if `state_management_save_previous` is set to `True`:
```python
s = await Sample.find_one(Sample.name == "Test")
s.num = 200
await s.save_changes()
s.has_changed == True
s.get_previous_changes() == {"num": 200}
s.get_changes() == {}
```
## Options
By default, state management will merge the changes made to nested objects,
which is fine for most cases as it is non-destructive and does not re-assign the whole object
if only one of its attributes changed:
```python
from typing import Dict
class Item(Document):
name: str
attributes: Dict[str, float]
class Settings:
use_state_management = True
```
```python
i = Item(name="Test", attributes={"attribute_1": 1.0, "attribute_2": 2.0})
await i.insert()
i.attributes = {"attribute_1": 1.0}
await i.save_changes()
# Changes will consist of: {"attributes.attribute_1": 1.0}
# Keeping attribute_2
```
However, there are some cases where you would want to replace the whole object when one of its attributes changed.
You can enable the `state_management_replace_objects` attribute in your model's `Settings` inner class:
```python
from typing import Dict
class Item(Document):
name: str
attributes: Dict[str, float]
class Settings:
use_state_management = True
state_management_replace_objects = True
```
With this setting activated, the whole object will be overridden when one attribute of the nested object is changed:
```python
i = Item(name="Test", attributes={"attribute_1": 1.0, "attribute_2": 2.0})
await i.insert()
i.attributes.attribute_1 = 1.0
await i.save_changes()
# Changes will consist of: {"attributes.attribute_1": 1.0, "attributes.attribute_2": 2.0}
# Keeping attribute_2
```
When the whole object is assigned, the whole nested object will be overridden:
```python
i = Item(name="Test", attributes={"attribute_1": 1.0, "attribute_2": 2.0})
await i.insert()
i.attributes = {"attribute_1": 1.0}
await i.save_changes()
# Changes will consist of: {"attributes": {"attribute_1": 1.0}}
# Removing attribute_2
```
|