Skip to content

Commit f92e4cc

Browse files
committed
break documentation out into separate section
1 parent cf8f8b0 commit f92e4cc

File tree

1 file changed

+49
-10
lines changed

1 file changed

+49
-10
lines changed

doc/examples/type_hints.rst

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,49 +92,88 @@ Note that when using :class:`~bson.son.SON`, the key and value types must be giv
9292
Typed Collection
9393
----------------
9494

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
9696
:class:`~pymongo.collection.Collection`. Note that all `schema validation`_ for inserts and updates is done on the server.
9797
These methods automatically add an "_id" field.
9898

9999
.. doctest::
100100

101-
>>> from typing import TypedDict
101+
>>> from typing_extensions import TypedDict
102102
>>> from pymongo import MongoClient
103103
>>> from pymongo.collection import Collection
104+
>>> from bson import ObjectId
104105
>>> class Movie(TypedDict):
105106
... name: str
106107
... year: int
107108
...
108109
>>> client: MongoClient = MongoClient()
109110
>>> collection: Collection[Movie] = client.test.test
111+
>>> collection.drop()
110112
>>> inserted = collection.insert_one(Movie(name="Jurassic Park", year=1993))
111-
>>> result = collection.find_one({"name": "Jurassic Park"})
113+
>>> result = collection.find_one({})
112114
>>> assert result is not None
113115
>>> assert result["year"] == 1993
114116
>>> # This will not be type checked, despite being present, because it is added by PyMongo.
115117
>>> assert type(result["_id"]) == ObjectId
116118

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+
119132

120133
.. doctest::
121134

122-
>>> from typing import TypedDict
135+
>>> from typing_extensions import TypedDict, NotRequired
123136
>>> from pymongo import MongoClient
124-
>>> from pymongo.operations import InsertOne
125137
>>> from pymongo.collection import Collection
138+
>>> from bson import ObjectId
126139
>>> class Movie(TypedDict):
127140
... name: str
128141
... year: int
129142
...
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+
...
130153
>>> client: MongoClient = MongoClient()
131154
>>> 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({})
134157
>>> assert result is not None
135-
>>> assert result["year"] == 1993
136158
>>> # This will not be type checked, despite being present, because it is added by PyMongo.
137159
>>> 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.
138177

139178

140179
Typed Database

0 commit comments

Comments
 (0)