Skip to main content

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

  • ObjectIdField
  • StringField
  • IntegerField
  • FloatField
  • BooleanField
  • DecimalField
  • DateTimeField
  • DictField
  • EmbeddedField
  • ListField
  • LiteralField
  • UnionField
  • EnumField
  • OptionalField[T] - Optional field that defaults to None

Optional Fields

Use mongo.OptionalField[T] to declare fields that:

  • Accept None values
  • Default to None when missing, in both partial and non-partial loads
  • Are omitted from dump() / to_mongo() when their value is None
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 user.nickname is None

Difference from allow_none=True:

  • Regular fields with allow_none=True still require a value (or explicit default)
  • OptionalField[T] fields automatically default to None if missing, in both partial and non-partial loads

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 == value
  • Document.field != value
  • Document.field > value
  • Document.field >= value
  • Document.field < value
  • Document.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 substring
  • StartsWith(value, case_sensitive=True) - Check if field starts with prefix
  • EndsWith(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 the Document.
    • collection: The collection of the Document.
    • use_session: Use session for the operations. (Use contextvars, 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.

Metadata

Release files for typedmongo 1.20.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for typedmongo 1.20.1
File Size Uploaded
typedmongo-1.20.1.tar.gz 49.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for typedmongo 1.20.1
File Interpreter ABI Platform
typedmongo-1.20.1-py3-none-any.whl Python 3 none any Details

Total release size: 84.1 kB

Release files / typedmongo-1.20.1.tar.gz

Download URL typedmongo-1.20.1.tar.gz
Size 49.9 kB
Tags Source
SHA-256 checksum
How to use checksums
98a7ffb088e68eb52a5f7a4f6ba4f6aa42bfc01367d505e2bdf0993223d04b99
BLAKE2b-256 checksum
How to use checksums
fe9e28a39ae2a5470233b6c4acd1b8bc4506bbca1662247d9513bb9f9d3ec609
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.

Transparency log

Release files / typedmongo-1.20.1-py3-none-any.whl

Download URL typedmongo-1.20.1-py3-none-any.whl
Size 34.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
107e63ccbe817de69bfb2800f74758f122e2e5e7196231c6b0eec895b88dfdd4
BLAKE2b-256 checksum
How to use checksums
7edecb3a661591b464d0fe61ecf4ea264d8b4fb55066268108debe64951994b1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.20.1 This release

2 release files

1.20.0

2 release files

1.19.0

2 release files

1.18.3

2 release files

1.18.2

2 release files

1.18.1

2 release files

1.16.1

2 release files

1.16.0

2 release files

1.15.3

2 release files

1.15.0

2 release files

1.14.0

2 release files

1.13.0

2 release files

1.12.0

2 release files

1.11.1

2 release files

1.11.0

2 release files

1.10.0

2 release files

1.9.0

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.1

2 release files

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page