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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
|
# Defining a document
The `Document` class in Beanie is responsible for mapping and handling the data
from the collection. It is inherited from the `BaseModel` Pydantic class, so it
follows the same data typing and parsing behavior.
```python
from typing import Optional
import pymongo
from pydantic import BaseModel
from beanie import Document, Indexed
class Category(BaseModel):
name: str
description: str
class Product(Document): # This is the model
name: str
description: Optional[str] = None
price: Indexed(float, pymongo.DESCENDING)
category: Category
class Settings:
name = "products"
indexes = [
[
("name", pymongo.TEXT),
("description", pymongo.TEXT),
],
]
```
## Fields
As it was mentioned before, the `Document` class is inherited from the Pydantic `BaseModel` class.
It uses all the same patterns of `BaseModel`. But also it has special types of fields:
- id
- Indexed
### id
`id` field of the `Document` class reflects the unique `_id` field of the MongoDB document.
Each object of the `Document` type has this field.
The default type of this is [PydanticObjectId](../api-documentation/fields.md/#pydanticobjectid).
```python
class Sample(Document):
num: int
description: str
foo = await Sample.find_one(Sample.num > 5)
print(foo.id) # This will print id
bar = await Sample.get(foo.id) # get by id
```
If you prefer another type, you can set it up too. For example, UUID:
```python
from uuid import UUID, uuid4
from pydantic import Field
class Sample(Document):
id: UUID = Field(default_factory=uuid4)
num: int
description: str
```
### Indexed
To set up an index over a single field, the `Indexed` function can be used to wrap the type:
```python
from beanie import Indexed
class Sample(Document):
num: Indexed(int)
description: str
```
The `Indexed` function takes an optional argument `index_type`, which may be set to a pymongo index type:
```python
class Sample(Document):
description: Indexed(str, index_type=pymongo.TEXT)
```
The `Indexed` function also supports pymongo `IndexModel` kwargs arguments ([PyMongo Documentation](https://pymongo.readthedocs.io/en/stable/api/pymongo/operations.html#pymongo.operations.IndexModel)).
For example, to create a `unique` index:
```python
class Sample(Document):
name: Indexed(str, unique=True)
```
## Settings
The inner class `Settings` is used to configure:
- MongoDB collection name
- Indexes
- Encoders
- Use of `revision_id`
- Use of cache
- Use of state management
- Validation on save
- Configure if nulls should be saved to the database
- Configure nesting depth for linked documents on the fetch operation
### Collection name
To set MongoDB collection name, you can use the `name` field of the `Settings` inner class.
```python
class Sample(Document):
num: int
description: str
class Settings:
name = "samples"
```
### Indexes
The `indexes` field of the inner `Settings` class is responsible for the indexes' setup.
It is a list where items can be:
- Single key. Name of the document's field (this is equivalent to using the Indexed function described above)
- List of (key, direction) pairs. Key - string, name of the document's field. Direction - pymongo direction (
example: `pymongo.ASCENDING`)
- `pymongo.IndexModel` instance - the most flexible
option. [PyMongo Documentation](https://pymongo.readthedocs.io/en/stable/api/pymongo/operations.html#pymongo.operations.IndexModel)
```python
class DocumentTestModelWithIndex(Document):
test_int: int
test_list: List[SubDocument]
test_str: str
class Settings:
indexes = [
"test_int",
[
("test_int", pymongo.ASCENDING),
("test_str", pymongo.DESCENDING),
],
IndexModel(
[("test_str", pymongo.DESCENDING)],
name="test_string_index_DESCENDING",
),
]
```
### Encoders
The `bson_encoders` field of the inner `Settings` class defines how the Python types are going to be represented
when saved in the database. The default conversions can be overridden with this.
The `ip` field in the following example is converted to String by default:
```python
from ipaddress import IPv4Address
class Sample(Document):
ip: IPv4Address
```
> **Note:** Default conversions are defined in `beanie.odm.utils.bson.ENCODERS_BY_TYPE`.
However, if you want the `ip` field to be represented as Integer in the database,
you need to override the default encoders like this:
```python
from ipaddress import IPv4Address
class Sample(Document):
ip: IPv4Address
class Settings:
bson_encoders = {
IPv4Address: int
}
```
You can also define your own function for the encoding:
```python
from ipaddress import IPv4Address
def ipv4address_to_int(v: IPv4Address):
return int(v)
class Sample(Document):
ip: IPv4Address
class Settings:
bson_encoders = {
IPv4Address: ipv4address_to_int
}
```
### Keep nulls
By default, Beanie saves fields with `None` value as `null` in the database.
But if you don't want to save `null` values, you can set `keep_nulls` to `False` in the `Settings` class:
```python
class Sample(Document):
num: int
description: Optional[str] = None
class Settings:
keep_nulls = False
```
### Nested Documents Depth
It is possible to define nested linked documents with Beanie. Sometimes this can lead to infinite recursion. To prevent this, or to decrease the database load, you can limit the maximum nesting depth. By default, it is set to 3, which means it will fetch up to 3 levels of nested documents.
You can configure:
- maximum depth for all linked documents
- depth for a specific linked document
Maximum:
```python
class Sample(Document):
num: int
category: Link[Category]
class Settings:
max_nesting_depth = 2
# Maximum nesting depth for all linked documents of this model
```
Specific:
```python
class Sample(Document):
num: int
category: Link[Category]
class Settings:
max_nesting_depths_per_field = {
"category": 1 # Nesting depth for a specific field
}
```
Also, you can limit the nesting depth during find operations. You can read more about this [here](/tutorial/relations/#nested-links).
|