The concept of MongoDB, SQLAlchemy and Pydantic.
Project description
mongotic
The concept of MongoDB, SQLAlchemy, and Pydantic combined together in one simple and effective solution. It enables you to use SQLAlchemy v2 query syntax with MongoDB, and allows you to define your data models using Pydantic.
Documentation: https://allen2c.github.io/mongotic/
Project management: Issues and epics are tracked with PLANK under
.plank/.
Overview
The mongotic library is designed to make working with MongoDB as seamless as possible by using familiar tools and patterns from the SQLAlchemy and Pydantic ecosystems. It provides a consistent and expressive way to interact with MongoDB collections, and utilises Pydantic for validation and data definition.
Features
- SQLAlchemy v2 API:
select(),session.scalars(),ScalarResult— familiar patterns without a SQL database. - Bulk Operations:
update()anddelete()statement builders viasession.execute(). - Data Validation: Utilise Pydantic's powerful schema definition for data validation and serialisation.
- Type Checking: Benefit from type checking and autocomplete in IDEs due to static type definitions.
- Works on standalone MongoDB: No replica set required — no multi-document transaction dependency.
Installation
pip install mongotic
Usage
v0.3.0 breaking change:
session.query()has been removed. Useselect()+session.scalars()instead.
from typing import Optional, Text
from pydantic import Field
from mongotic import MultipleResultsFound, NotFound, create_engine, delete, select, update
from mongotic.model import MongoBaseModel
from mongotic.orm import sessionmaker
class User(MongoBaseModel):
__databasename__ = "test_database"
__tablename__ = "user"
name: Text = Field(..., max_length=50)
email: Text = Field(...)
company: Optional[Text] = Field(None, max_length=50)
age: Optional[int] = Field(None, ge=0, le=200)
engine = create_engine("mongodb://localhost:27017")
Session = sessionmaker(bind=engine)
# ── Add ──────────────────────────────────────────────────────────────────────
session = Session()
session.add(User(name="Allen Chou", email="allen@example.com", company="Acme", age=30))
session.add_all([
User(name="Bob", email="bob@example.com", company="Acme", age=25),
User(name="Carol", email="carol@example.com", company="Acme", age=28),
])
session.commit()
# ── Query ────────────────────────────────────────────────────────────────────
session = Session()
# Fetch all / first
users = session.scalars(select(User)).all()
users = session.scalars(select(User).where(User.age > 18)).all()
users = session.scalars(
select(User)
.where(User.company == "Acme")
.order_by(-User.age) # descending; use User.age for ascending
.limit(10)
.offset(0)
).all()
user = session.scalars(select(User).where(User.email == "allen@example.com")).first()
user = session.get(User, "<object_id_string>") # PK lookup; returns None if not found
# Strict single-result fetch
try:
user = session.scalars(select(User).where(User.email == "allen@example.com")).one()
# raises NotFound if 0 results; raises MultipleResultsFound if 2+ results
except NotFound:
...
except MultipleResultsFound:
...
user = session.scalars(
select(User).where(User.email == "allen@example.com")
).one_or_none()
# returns None if 0 results; raises MultipleResultsFound if 2+ results
# Count and existence check
count = session.scalars(select(User).where(User.company == "Acme")).count()
exists = session.scalars(select(User).where(User.company == "Acme")).exists()
# ── Update ───────────────────────────────────────────────────────────────────
session = Session()
user = session.scalars(select(User).where(User.email == "allen@example.com")).first()
user.email = "new.allen@example.com" # tracked automatically
session.commit()
# ── Delete ───────────────────────────────────────────────────────────────────
session = Session()
user = session.scalars(select(User).where(User.email == "new.allen@example.com")).first()
session.delete(user)
session.commit()
# ── Bulk Operations ──────────────────────────────────────────────────────────
session = Session()
# Bulk update: returns number of modified documents
modified = session.execute(
update(User).where(User.company == "Acme").values(company="Acme Corp")
)
# Bulk delete: returns number of deleted documents
deleted = session.execute(
delete(User).where(User.age < 18)
)
# ── Context manager + flush ──────────────────────────────────────────────────
with Session() as session:
new_user = User(name="Dave", email="dave@example.com", age=35)
session.add(new_user)
session.flush() # writes immediately; new_user._id is now available
print(new_user._id)
session.commit() # alias for flush()
Contributing
TODO
License
This project is licensed under the MIT License — see the LICENSE file for details.
Support
If you encounter any problems or have suggestions, please open an issue, or feel free to reach out directly.
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
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 mongotic-0.3.0.tar.gz.
File metadata
- Download URL: mongotic-0.3.0.tar.gz
- Upload date:
- Size: 8.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.11.14 Darwin/25.3.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7280c33134de3bbee39da82f76b63bcbc004df72f1727fe3de91b1262b14549
|
|
| MD5 |
9cc235b6fd59e7fb5240a6ae9787b600
|
|
| BLAKE2b-256 |
f328e2d1a2b7e2a2a1c0863dbc0f735c681d368892d561115a900d49c8e08fa7
|
File details
Details for the file mongotic-0.3.0-py3-none-any.whl.
File metadata
- Download URL: mongotic-0.3.0-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.2 CPython/3.11.14 Darwin/25.3.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d87ec169e92e03c45db54052a21c474fc4bcbbdec83a3d1b65560099b34a63a7
|
|
| MD5 |
6bf5377fb8acfff816cfed7b07a86ecb
|
|
| BLAKE2b-256 |
b8a1d7ea4346da3cdde0a047d3415dda17db7cfa8c55ecd3141d91de38105a1e
|