Skip to main content

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

  • 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 in 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=True still require a value (or explicit default)
  • OptionalField[T] fields automatically default to None in 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 == 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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typedmongo-1.19.0.tar.gz (50.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typedmongo-1.19.0-py3-none-any.whl (34.3 kB view details)

Uploaded Python 3

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

Hashes for typedmongo-1.19.0.tar.gz
Algorithm Hash digest
SHA256 36e511b037c336e9bff5f4570084642bd6bef5427a6b0524fb0fb02b1e71525b
MD5 1ecffe27a280cdf10ef822ed8e3d20b8
BLAKE2b-256 48c43fa1e1b180de5f22ccaaf03ffb1b49c655e5e0a57fa27e880947e6ec60d8

See more details on using hashes here.

Provenance

The following attestation bundles were made for typedmongo-1.19.0.tar.gz:

Publisher: ci.yml on abersheeran/typedmongo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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

Hashes for typedmongo-1.19.0-py3-none-any.whl
Algorithm Hash digest
SHA256 dbf4d01e9afcace8254fca3feb0203c4cea005d55cfe175b728c9d49283a41cf
MD5 5de49660b72e3ebbf1a1238d7e89b614
BLAKE2b-256 f142f2a19b1107a0b9059da3c69dd344bb351ac1c49ae5bb736822134fb91d10

See more details on using hashes here.

Provenance

The following attestation bundles were made for typedmongo-1.19.0-py3-none-any.whl:

Publisher: ci.yml on abersheeran/typedmongo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page