Skip to main content

SOLID, extensible MongoDB client for Python using Motor (AsyncCollectionProtocol + RepositoryProtocol TCreate/TUpdate/TOut)

Project description

ildev-mongodb

SOLID, extensible MongoDB client for Python using Motor. Dict-based AsyncCollectionProtocol and typed RepositoryProtocol (TCreate / TUpdate / TOut + to_doc_create, to_doc_update, from_doc_out). Supports append-only transactional repositories, base models, aggregation, localization and JSON logging. Dependency Inversion and DI.

Requires: Python ≥3.10, Motor.

Install

pip install ildev-mongodb

Layers

  1. AsyncCollectionProtocol – base protocol, document: dict[str, Any]. Full CRUD + aggregate.
  2. RepositoryProtocol[TCreate, TUpdate, TOut] – typed CRUD + explicit transforms: to_doc_create, to_doc_update, from_doc_out + aggregate -> list[TOut].
  3. TransactionalRepositoryProtocol[TCreate, TOut] – append-only (insert + read + aggregate), no update/delete.
  4. BaseAsyncCollection – implements AsyncCollectionProtocol (dict-based).
  5. BaseTypedRepository – implements RepositoryProtocol; wraps AsyncCollectionProtocol and uses the three transforms.
  6. TransactionalTypedRepository – implements TransactionalRepositoryProtocol on top of BaseAsyncCollection.

Quick start (dict-based)

import asyncio
from ildev_mongodb import create_async_client

async def main():
    async with create_async_client("mongodb://localhost:27017/") as client:
        db = client.get_database("mydb")
        coll = db.get_collection("items")
        await coll.insert_one({"name": "test"})
        doc = await coll.find_one({"name": "test"})
        print(doc)

asyncio.run(main())

Quick start (typed TCreate/TUpdate/TOut)

import asyncio
from ildev_mongodb import create_async_client, BaseTypedRepository

class Item:
    def __init__(self, name: str, value: int): ...
    def to_doc(self): ...
    @classmethod
    def from_doc(cls, d: dict): ...

async def main():
    async with create_async_client("mongodb://localhost:27017/") as client:
        raw = client.get_database("mydb").get_collection("items")
        items = BaseTypedRepository(
            raw,
            to_doc_create=lambda x: x.to_doc(),
            to_doc_update=lambda u: u if isinstance(u, dict) else u.to_doc(),
            from_doc_out=Item.from_doc,
        )
        await items.insert_one(Item("x", 1))
        one = await items.find_one({"name": "x"})
        await items.update_one({"name": "x"}, {"$set": {"value": 2}})
        async for doc in items.find():
            print(doc.name, doc.value)

asyncio.run(main())

Quick start (transactional / append-only repository)

Use TransactionalTypedRepository when you only want insert + read + aggregate (event-sourcing / append-only):

import asyncio
from dataclasses import dataclass
from datetime import datetime, timezone
from ildev_mongodb import (
    create_async_client,
    TransactionalTypedRepository,
    TransactionalCreateBase,
    TransactionalOutBase,
)


@dataclass
class EventCreate(TransactionalCreateBase):
    name: str = ""


@dataclass
class EventOut(TransactionalOutBase):
    name: str = ""


def to_doc_create(e: EventCreate) -> dict[str, object]:
    return {"name": e.name, "created_at": e.created_at}


def from_doc_out(d: dict[str, object]) -> EventOut:
    return EventOut(name=str(d["name"]))


async def main() -> None:
    async with create_async_client("mongodb://localhost:27017/") as client:
        raw = client.get_database("mydb").get_collection("events")
        events = TransactionalTypedRepository[EventCreate, EventOut](
            raw,
            to_doc_create=to_doc_create,
            from_doc_out=from_doc_out,
        )
        await events.insert_one(EventCreate(name="OrderCreated"))
        one = await events.find_one()
        print(one.name)


asyncio.run(main())

Full CRUD

AsyncCollectionProtocol (dict-based): insert_one, insert_many, find_one, find, update_one, update_many, delete_one, delete_many, count_documents.

RepositoryProtocol[TCreate, TUpdate, TOut]: same CRUD with typed payloads; exposes to_doc_create, to_doc_update, from_doc_out and .collection.

BaseTypedRepository implements RepositoryProtocol by wrapping AsyncCollectionProtocol (e.g. BaseAsyncCollection) and the three transform callables.

Base models

To standardize common fields, you can subclass these dataclasses from ildev_mongodb.models:

  • TypedCreateBaseid: UUID (auto-generated) + created_at: datetime (UTC).
  • TypedOutBaseid: UUID | None, created_at: datetime, updated_at: datetime.
  • TypedUpdateBaseupdated_at: datetime.
  • TransactionalCreateBase – same as TypedCreateBase, for transactional repos.
  • TransactionalOutBaseid: UUID | None, created_at: datetime (no updated_at).

Example:

from dataclasses import dataclass
from ildev_mongodb import TypedCreateBase, TypedOutBase


@dataclass
class ItemCreate(TypedCreateBase):
    name: str = ""


@dataclass
class ItemOut(TypedOutBase):
    name: str = ""

The repository type parameters must be subclasses of these base models (TCreate bound to TypedCreateBase, etc.).

Aggregation

All layers support aggregation:

  • AsyncCollectionProtocol.aggregate(pipeline) -> list[dict[str, Any]]
  • BaseAsyncCollection.aggregate(...) – wraps Motor’s aggregate and excludes _id from results.
  • RepositoryProtocol.aggregate(pipeline) -> list[TOut]
  • BaseTypedRepository.aggregate(...) / TransactionalTypedRepository.aggregate(...) – map each doc via from_doc_out.

Example:

results = await items.aggregate(
    [
        {"$match": {"name": "A"}},
        {"$project": {"_id": 0, "name": 1, "value": 1}},
    ]
)

Localization

All public exception messages can be localized via a simple JSON file:

{
  "operation_failed": "Operace selhala.",
  "connection_failed": "Připojení k databázi selhalo.",
  "unsupported_operation": "Operace není podporována."
}

Configure once at startup:

from ildev_mongodb import configure_localization

configure_localization("config/ildev_mongodb.locale.json")

Exceptions that honor localization:

  • IldevMongoDBError (operation_failed)
  • IldevMongoDBConnectionError (connection_failed)
  • UnsupportedOperationError (unsupported_operation)

Passing an explicit message= to these constructors bypasses localization.

JSON logging

The library logs errors via the standard logging module under the logger name ildev_mongodb.

Configure JSON logging destination once at startup:

from ildev_mongodb import configure_logging

# Write JSON lines to a file
configure_logging("logs/ildev_mongodb.log", level="INFO")

# Or, to stderr by default
configure_logging()

Each log is a single JSON line, e.g.:

{"timestamp": "...", "level": "ERROR", "logger": "ildev_mongodb", "message": "database_operation_failed", "event": "db_error", "operation": "insert_one", "exc_type": "SomeMotorError", "error_class": "IldevMongoDBError"}

The library never logs URIs, full documents, or credentials; logging focuses on safe, structured error context only.

Direct client construction

from ildev_mongodb import BaseAsyncClient

async def main():
    client = BaseAsyncClient(uri="mongodb://localhost:27017/")
    try:
        db = client.get_database("mydb")
        coll = db.get_collection("items")
        # ...
    finally:
        await client.close()

License

MIT

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

ildev_mongodb-0.1.1.tar.gz (15.6 kB view details)

Uploaded Source

Built Distribution

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

ildev_mongodb-0.1.1-py3-none-any.whl (19.9 kB view details)

Uploaded Python 3

File details

Details for the file ildev_mongodb-0.1.1.tar.gz.

File metadata

  • Download URL: ildev_mongodb-0.1.1.tar.gz
  • Upload date:
  • Size: 15.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for ildev_mongodb-0.1.1.tar.gz
Algorithm Hash digest
SHA256 03b02578d7ac5e7d3b398a7c400daf261cec0247f96b3052014d7274473bf029
MD5 283cb16db1535c82b3336bfefa1a7956
BLAKE2b-256 ca20cf9dbc63543fa382f6dcf469ae050b095c55b765b8676124149ad7f5438d

See more details on using hashes here.

File details

Details for the file ildev_mongodb-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: ildev_mongodb-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 19.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for ildev_mongodb-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9b6e40192b378198d327916d22284bd2f565a20b22f080fc975b99b97468996e
MD5 4a4d045d17b02815b540328480478060
BLAKE2b-256 e4a68976ac0596ea43c510cb064dbe84b2713651c759aea174b19219157a23b4

See more details on using hashes here.

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