Minimal async MongoDB ODM built for speed and simplicity, featuring automatic collection binding, msgspec integration, and first-class asyncio support.
pip install mongospecRequires Python 3.13+ and a running MongoDB 6.0+ server.
import asyncio
from datetime import datetime
from typing import ClassVar, Sequence
import mongojet
import msgspec
import mongospec
from mongospec import MongoDocument
from mongospec import IndexModel
class User(MongoDocument):
__collection_name__ = "users"
__indexes__: ClassVar[Sequence[IndexModel]] = [
IndexModel(keys=[("email", 1)], options={"unique": True})
]
name: str
email: str
created_at: datetime = msgspec.field(default_factory=datetime.now)
async def main() -> None:
client = await mongojet.create_client("mongodb://localhost:27017")
await mongospec.init(client.get_database("example_db"), document_types=[User])
user = User(name="Alice", email="alice@example.com")
await user.insert()
print("Inserted:", user)
fetched = await User.find_one({"email": "alice@example.com"})
print("Fetched:", fetched)
await fetched.delete()
await mongospec.close()
if __name__ == "__main__":
asyncio.run(main())All other usage examples have been moved to standalone scripts in the
examples/ directory.
Each file is self-contained and can be executed directly:
| Script | What it covers |
|---|---|
quick_start.py |
End-to-end “hello world” |
document_models.py |
Defining typed models & indexes |
connection_management.py |
Initialising the ODM and binding collections |
collection_binding.py |
Using models immediately after init |
index_creation.py |
Unique, compound & text indexes |
create_documents.py |
Single & bulk inserts, conditional insert |
read_documents.py |
Queries, cursors, projections |
update_documents.py |
Field updates, atomic & versioned updates |
delete_documents.py |
Single & batch deletes |
count_documents.py |
Fast counts & estimated counts |
working_with_cursors.py |
Batch processing large result sets |
batch_operations.py |
Bulk insert / update / delete |
atomic_updates.py |
Optimistic-locking with version field |
upsert_operations.py |
Upsert via save and update_one |
projection_example.py |
Field selection for performance |
- Zero-boilerplate models – automatic collection resolution & binding.
- Async first – built on
mongojet, fullyawait-able API. - Typed & fast – data classes powered by
msgspecfor ultra-fast (de)serialization. - Declarative indexes – define indexes right on the model with
familiar
pymongo/mongojetIndexModels. - Batteries included – helpers for common CRUD patterns, bulk and atomic operations, cursors, projections, upserts and more.
Define your schema by subclassing MongoDocument
and adding typed attributes.
See examples/document_models.py.
Initialise once with mongospec.init(...), passing a
mongojet.Database and the list of models to bind.
See examples/connection_management.py.
After initialisation every model knows its collection and can be used
immediately – no manual wiring required.
See examples/collection_binding.py.
The MongoDocument class (and its mixins) exposes a rich async CRUD API:
insert, find, update, delete, count, cursors, bulk helpers,
atomic find_one_and_update, upserts, etc.
See scripts in examples/ grouped by operation type.
Declare indexes in __indexes__ as a Sequence[IndexModel]
(unique, compound, text, …).
Indexes are created automatically at init time.
See examples/index_creation.py.
In addition to manually listing document classes when calling mongospec.init(...), you can use the utility function collect_document_types(...) to automatically discover all models in a package:
from mongospec.utils import collect_document_types
document_types = collect_document_types("myapp.db.models")
await mongospec.init(db, document_types=document_types)This function supports:
- Recursive import of all submodules in the target package
- Filtering by base class (default:
MongoDocument) - Optional exclusion of abstract or re-exported classes
- Regex or callable-based module filtering
- Graceful handling of import errors
Usage Example:
from mongospec.utils import collect_document_types
# Collect all document models in `myapp.db.models` and its submodules
models = collect_document_types(
"myapp.db.models",
ignore_abstract=True,
local_only=True,
on_error="warn",
)
await mongospec.init(db, document_types=models)Advanced options include:
predicate=...to filter only specific model typesreturn_map=Trueto get a{qualified_name: class}dictmodule_filter=".*models.*"to restrict traversal
See the full function signature in mongospec/utils.py.