A production-ready modern Python MongoDB ODM
Project description
Typed Mongo
A production-ready modern Python MongoDB ODM
In addition to synchronous mode, you can use asynchronous mode, just export from typedmongo.asyncio.
Install
pip install typedmongo
Usage
Usage examples trump all usage documentation. So please look at the Example below first.
Example
import datetime
from typing import Literal
from pymongo import AsyncMongoClient as MongoClient
import typedmongo.asyncio as mongo
class Wallet(mongo.Document):
balance: mongo.DecimalField
class User(mongo.MongoDocument):
name: mongo.StringField
gender: mongo.LiteralField[Literal["m", "f"]]
age: mongo.IntegerField
tags: mongo.ListField[str]
wallet: mongo.EmbeddedField[Wallet]
created_at: mongo.DateTimeField = mongo.DateTimeField(
default=lambda: datetime.datetime.now(datetime.timezone.utc)
)
children: mongo.ListField[User]
extra: mongo.DictField = mongo.DictField(default=dict)
# Optional fields
nickname: mongo.OptionalField[str]
bio: mongo.OptionalField[str]
async def main():
await mongo.initial_collections(
MongoClient().mongo,
User,
)
# Insert one document
document_id = await User.objects.insert_one(
User.load(
{
"name": "Aber",
"gender": "m",
"age": 18,
"tags": ["a", "b"],
"wallet": {"balance": 100},
"children": [],
},
)
)
# Find one document
user = await User.objects.find_one(User._id == document_id, sort=[+User.age])
# Update one document
update_result = await User.objects.update_one(
User._id == document_id, {"$set": {"tags": ["a", "b", "e", "r"]}}
)
# Delete one document
delete_result = await User.objects.delete_one(User._id == document_id)
# Find one and update
user = await User.objects.find_one_and_update(
User._id == document_id, {"$set": {"tags": ["a", "b", "e"]}}
)
# Find one and replace
user = await User.objects.find_one_and_replace(
User._id == document_id,
User.load({"name": "Aber", "age": 0}),
after_document=True,
)
# Find one and delete
user = await User.objects.find_one_and_delete(User._id == document_id)
# Find many documents and sort
users = [user async for user in User.objects.find(User.age == 18, sort=[-User.age])]
# Update many documents
update_result = await User.objects.update_many(
User.wallet._.balance == Decimal("100"), {"$inc": {"wallet.balance": 10}}
)
# Count documents
await User.objects.count_documents(User.age >= 0)
# Bulk write operations
await User.objects.bulk_write(
mongo.DeleteOne(User._id == 0),
mongo.DeleteMany(User.age < 18),
mongo.InsertOne(User.load({"name": "InsertOne"}, partial=True)),
mongo.ReplaceOne(User.name == "Aber", User.load({}, partial=True)),
mongo.UpdateMany({}, {"$set": {"age": 25}}),
mongo.UpdateMany(User.name == "Yue", {"$set": {"name": "yue"}}),
)
Document
Note: The Document must be initialized with initial_collections before it can be used.
Document.load: Load data from dict to instance, and validate the data.Document.dump: Dump the instance to jsonable dict.
Collection Name
Document.__collection_name__: Normally, subclasses of Document will generate a collection_name based on the Class Name, but if you want to customize it, you can set __collection_name__ when defining it.
class APIKey(mongo.Document):
__collection_name__ = "api_key"
Raw Collection
If you want to use functions such as aggregate, you can access pymongo's original collection object through Document.objects.collection.
Document.objects.collection.aggregate([
{"$group": {"_id": "$field", "count": {"$sum": 1}}}
])
Field
ObjectIdFieldStringFieldIntegerFieldFloatFieldBooleanFieldDecimalFieldDateTimeFieldDictFieldEmbeddedFieldListFieldLiteralFieldUnionFieldEnumFieldOptionalField[T]- Optional field that defaults to None
Optional Fields
Use mongo.OptionalField[T] to declare fields that:
- Accept
Nonevalues - Default to
Nonein non-partial loads - Remain unset in partial loads
class User(mongo.Document):
name: mongo.StringField # Required
nickname: mongo.OptionalField[str] # Optional, defaults to None
age: mongo.OptionalField[int]
bio: mongo.OptionalField[str]
wallet: mongo.OptionalField[Wallet] # Optional embedded document
tags: mongo.OptionalField[list[str]] # Optional list
# Create with only required fields
user = User(name="Alice")
assert user.nickname is None
# Load without optional fields (non-partial)
user = User.load({"name": "Bob"})
assert user.nickname is None
# Load without optional fields (partial)
user = User.load({"name": "Charlie"}, partial=True)
assert not hasattr(user, "nickname") # Unset
Difference from allow_none=True:
- Regular fields with
allow_none=Truestill require a value (or explicit default) OptionalField[T]fields automatically default toNonein non-partial loads- In partial loads,
OptionalField[T]fields remain unset if missing
Conditional expressions
If you want to use conditional expressions with methods like aggregate, you can call expression.compile() to get a mongo expression.
Document.objects.collection.aggregate([
{"$match": (Document.age >= 18).compile()},
{"$group": {"_id": "$field", "count": {"$sum": 1}}},
])
Comparison expressions
Document.field == valueDocument.field != valueDocument.field > valueDocument.field >= valueDocument.field < valueDocument.field <= value
Logical expressions
(Document.field == value) & (Document.field == value)(Document.field == value) | (Document.field == value)~(Document.field == value)~((Document.field == value) & (Document.field == value))~((Document.field == value) | (Document.field == value))
String matching shortcuts
Contains(value, case_sensitive=True)- Check if field contains substringStartsWith(value, case_sensitive=True)- Check if field starts with prefixEndsWith(value, case_sensitive=True)- Check if field ends with suffix
from typedmongo.asyncio import Contains, StartsWith, EndsWith
# Find users whose name contains "John"
users = User.objects.find(User.name == Contains("John"))
# Case-insensitive search
users = User.objects.find(User.name == Contains("john", case_sensitive=False))
# Starts with / Ends with
users = User.objects.find(User.name == StartsWith("A"))
users = User.objects.find(User.email == EndsWith("@example.com"))
RawExpression
Sometime, you maybe need use raw query, you can use RawExpression to do that.
from typedmongo.asyncio import RawExpression
# Or `from typedmongo import RawExpression`
User.objects.find(RawExpression({"field_name": {"$mongo_command": value}}) & User.age > 18)
Sort expressions
+Document.field: Ascending-Document.field: Descending
User.objects.find(..., sort=[+User.age, -User.name])
Objects
Document.objects: The object manager of theDocument.collection: The collection of theDocument.use_session: Use session for the operations. (Usecontextvars, so you don't need to pass the session to the function parameters)use_transaction: Use transaction for the operations.insert_one: Insert one document.insert_many: Insert many documents.find: Find many documents.find_one: Find one document.find_one_and_update: Find one and update.find_one_and_replace: Find one and replace.find_one_and_delete: Find one and delete.delete_one: Delete one document.delete_many: Delete many documents.update_one: Update one document.update_many: Update many documents.count_documents: Count documents.bulk_write: Bulk write operations.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file typedmongo-1.19.0.tar.gz.
File metadata
- Download URL: typedmongo-1.19.0.tar.gz
- Upload date:
- Size: 50.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
36e511b037c336e9bff5f4570084642bd6bef5427a6b0524fb0fb02b1e71525b
|
|
| MD5 |
1ecffe27a280cdf10ef822ed8e3d20b8
|
|
| BLAKE2b-256 |
48c43fa1e1b180de5f22ccaaf03ffb1b49c655e5e0a57fa27e880947e6ec60d8
|
Provenance
The following attestation bundles were made for typedmongo-1.19.0.tar.gz:
Publisher:
ci.yml on abersheeran/typedmongo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
typedmongo-1.19.0.tar.gz -
Subject digest:
36e511b037c336e9bff5f4570084642bd6bef5427a6b0524fb0fb02b1e71525b - Sigstore transparency entry: 1075078069
- Sigstore integration time:
-
Permalink:
abersheeran/typedmongo@fcd703599944cd92a87878e878747d9e4ee981fa -
Branch / Tag:
refs/tags/v1.19.0 - Owner: https://github.com/abersheeran
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@fcd703599944cd92a87878e878747d9e4ee981fa -
Trigger Event:
push
-
Statement type:
File details
Details for the file typedmongo-1.19.0-py3-none-any.whl.
File metadata
- Download URL: typedmongo-1.19.0-py3-none-any.whl
- Upload date:
- Size: 34.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dbf4d01e9afcace8254fca3feb0203c4cea005d55cfe175b728c9d49283a41cf
|
|
| MD5 |
5de49660b72e3ebbf1a1238d7e89b614
|
|
| BLAKE2b-256 |
f142f2a19b1107a0b9059da3c69dd344bb351ac1c49ae5bb736822134fb91d10
|
Provenance
The following attestation bundles were made for typedmongo-1.19.0-py3-none-any.whl:
Publisher:
ci.yml on abersheeran/typedmongo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
typedmongo-1.19.0-py3-none-any.whl -
Subject digest:
dbf4d01e9afcace8254fca3feb0203c4cea005d55cfe175b728c9d49283a41cf - Sigstore transparency entry: 1075078117
- Sigstore integration time:
-
Permalink:
abersheeran/typedmongo@fcd703599944cd92a87878e878747d9e4ee981fa -
Branch / Tag:
refs/tags/v1.19.0 - Owner: https://github.com/abersheeran
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@fcd703599944cd92a87878e878747d9e4ee981fa -
Trigger Event:
push
-
Statement type: