@@ -92,49 +92,88 @@ Note that when using :class:`~bson.son.SON`, the key and value types must be giv
92
92
Typed Collection
93
93
----------------
94
94
95
- You can use :py:class: `~typing .TypedDict ` (Python 3.8+) when using a well-defined schema for the data in a
95
+ You can use :py:class: `~typing_extensions .TypedDict ` (Python 3.8+) when using a well-defined schema for the data in a
96
96
:class: `~pymongo.collection.Collection `. Note that all `schema validation `_ for inserts and updates is done on the server.
97
97
These methods automatically add an "_id" field.
98
98
99
99
.. doctest ::
100
100
101
- >>> from typing import TypedDict
101
+ >>> from typing_extensions import TypedDict
102
102
>>> from pymongo import MongoClient
103
103
>>> from pymongo.collection import Collection
104
+ >>> from bson import ObjectId
104
105
>>> class Movie (TypedDict ):
105
106
... name: str
106
107
... year: int
107
108
...
108
109
>>> client: MongoClient = MongoClient()
109
110
>>> collection: Collection[Movie] = client.test.test
111
+ >>> collection.drop()
110
112
>>> inserted = collection.insert_one(Movie(name = " Jurassic Park" , year = 1993 ))
111
- >>> result = collection.find_one({" name " : " Jurassic Park " })
113
+ >>> result = collection.find_one({})
112
114
>>> assert result is not None
113
115
>>> assert result[" year" ] == 1993
114
116
>>> # This will not be type checked, despite being present, because it is added by PyMongo.
115
117
>>> assert type (result[" _id" ]) == ObjectId
116
118
117
- This same typing scheme works for all of the insert methods (`insert_one `, `insert_many `, and `bulk_write `). For `bulk_write `,
118
- both `InsertOne/Many ` and `ReplaceOne/Many ` operators are generic.
119
+ Modeling Document Types with TypedDict
120
+ --------------------------------------
121
+
122
+ You can use :py:class: `~typing_extensions.TypedDict ` (Python 3.8+) to model structured data.
123
+ As noted above, PyMongo will automatically add an `_id ` field if it is not present. This also applies to TypedDict.
124
+ There are three approaches to this:
125
+
126
+ 1. Do not specify `_id ` at all. It will be inserted automatically, and can be retrieved at run-time, but cannot be type-checked.
127
+
128
+ 2. Specify `_id ` explicitly. This will mean that every instance of your custom TypedDict class will have to passed a value for `_id `.
129
+
130
+ 3. Make use of :py:class: `~typing_extensions.NotRequired `. This has the flexibility of 1, but with the ability to type-check.
131
+
119
132
120
133
.. doctest ::
121
134
122
- >>> from typing import TypedDict
135
+ >>> from typing_extensions import TypedDict, NotRequired
123
136
>>> from pymongo import MongoClient
124
- >>> from pymongo.operations import InsertOne
125
137
>>> from pymongo.collection import Collection
138
+ >>> from bson import ObjectId
126
139
>>> class Movie (TypedDict ):
127
140
... name: str
128
141
... year: int
129
142
...
143
+ >>> class ExplicitMovie (TypedDict ):
144
+ ... _id: ObjectId
145
+ ... name: str
146
+ ... year: int
147
+ ...
148
+ >>> class NotRequiredMovie (TypedDict ):
149
+ ... _id: NotRequired[ObjectId]
150
+ ... name: str
151
+ ... year: int
152
+ ...
130
153
>>> client: MongoClient = MongoClient()
131
154
>>> collection: Collection[Movie] = client.test.test
132
- >>> inserted = collection.bulk_write([InsertOne( Movie(name = " Jurassic Park" , year = 1993 ))] )
133
- >>> result = collection.find_one({" name " : " Jurassic Park " })
155
+ >>> inserted = collection.insert_one( Movie(name = " Jurassic Park" , year = 1993 ))
156
+ >>> result = collection.find_one({})
134
157
>>> assert result is not None
135
- >>> assert result[" year" ] == 1993
136
158
>>> # This will not be type checked, despite being present, because it is added by PyMongo.
137
159
>>> assert type (result[" _id" ]) == ObjectId
160
+ >>> collection: Collection[ExplicitMovie] = client.test.test
161
+ >>> # Note that the _id keyword argument must be supplied
162
+ >>> inserted = collection.insert_one(ExplicitMovie(_id = ObjectId(), name = " Jurassic Park" , year = 1993 ))
163
+ >>> result = collection.find_one({})
164
+ >>> assert result is not None
165
+ >>> # This will be type checked.
166
+ >>> assert type (result[" _id" ]) == ObjectId
167
+ >>> collection: Collection[NotRequiredMovie] = client.test.test
168
+ >>> # Note the lack of _id, similar to the first example
169
+ >>> inserted = collection.insert_one(NotRequiredMovie(name = " Jurassic Park" , year = 1993 ))
170
+ >>> result = collection.find_one({})
171
+ >>> assert result is not None
172
+ >>> # This will be type checked, despite not being provided explicitly.
173
+ >>> assert type (result[" _id" ]) == ObjectId
174
+
175
+ This same typing scheme works for all of the insert methods (`insert_one `, `insert_many `, and `bulk_write `). For `bulk_write `,
176
+ both `InsertOne/Many ` and `ReplaceOne/Many ` operators are generic.
138
177
139
178
140
179
Typed Database
0 commit comments