Differences between Motor and PyMongo¶
Warning
As of May 14th, 2025, Motor is deprecated in favor of the GA release of the PyMongo Async API. No new features will be added to Motor, and only bug fixes will be provided until it reaches end of life on May 14th, 2026. After that, only critical bug fixes will be made until final support ends on May 14th, 2027. We strongly recommend migrating to the PyMongo Async API while Motor is still supported. For help transitioning, see the Migrate to PyMongo Async guide.
Important
This page describes using Motor with Tornado. Beginning in version 0.5 Motor can also integrate with asyncio instead of Tornado.
Major differences¶
Connecting to MongoDB¶
Motor provides a single client class, MotorClient. Unlike PyMongo’s
MongoClient, Motor’s client class does
not begin connecting in the background when it is instantiated. Instead it
connects on demand, when you first attempt an operation.
Coroutines¶
Motor supports nearly every method PyMongo does, but Motor methods that do network I/O are coroutines. See Tutorial: Using Motor With Tornado.
Threading and forking¶
Multithreading and forking are not supported; Motor is intended to be used in a single-threaded Tornado application. See Tornado’s documentation on running Tornado in production to take advantage of multiple cores.
Minor differences¶
GridFS¶
File-like
PyMongo’s
GridInandGridOutstrive to act like Python’s built-in file objects, so they can be passed to many functions that expect files. But the I/O methods ofMotorGridInandMotorGridOutare asynchronous, so they cannot obey the file API and aren’t suitable in the same circumstances as files.Setting properties
In PyMongo, you can set arbitrary attributes on a
GridInand they’re stored as metadata on the server, even after theGridInis closed:fs = gridfs.GridFSBucket(db) grid_in = fs.open_upload_stream('test_file') grid_in.close() grid_in.my_field = 'my_value' # Sends update to server.
Updating metadata on a
MotorGridInis asynchronous, so the API is different:async def f(): fs = motor.motor_tornado.MotorGridFSBucket(db) grid_in = fs.open_upload_stream('test_file') await grid_in.close() # Sends update to server. await grid_in.set('my_field', 'my_value')
See also
is_locked¶
In PyMongo is_locked is a property of
MongoClient. Since determining whether the
server has been fsyncLocked requires I/O, Motor has no such convenience method.
The equivalent in Motor is:
result = await client.admin.current_op()
locked = bool(result.get('fsyncLock', None))
system_js¶
PyMongo supports Javascript procedures stored in MongoDB with syntax like:
>>> db.system_js.my_func = "function(x) { return x * x; }"
>>> db.system_js.my_func(2)
4.0
Motor does not.
Cursor slicing¶
In PyMongo, the following raises an IndexError if the collection has fewer
than 101 documents:
# Can raise IndexError.
doc = db.collection.find()[100]
In Motor, however, no exception is raised. The query simply has no results:
async def f():
cursor = db.collection.find()[100]
# Iterates zero or one time.
async for doc in cursor:
print(doc)
The difference arises because the PyMongo Cursor’s
slicing operator blocks until it has queried the MongoDB server, and determines
if a document exists at the desired offset; Motor simply returns a new
MotorCursor with a skip and limit applied.
Creating a collection¶
There are two ways to create a capped collection using PyMongo:
# Typical:
db.create_collection("collection1", capped=True, size=1000)
# Unusual:
collection = Collection(db, "collection2", capped=True, size=1000)
Motor can’t do I/O in a constructor, so the unusual style is prohibited and only the typical style is allowed:
async def f():
await db.create_collection("collection1", capped=True, size=1000)