Metadata-Version: 2.4
Name: mongoforge-odm
Version: 0.1.1
Summary: A Django-inspired MongoDB ODM for Python
Author: Saurabh Singh
License-Expression: MIT
Project-URL: Homepage, https://github.com/Sourabh7singh/MongoForge
Project-URL: Repository, https://github.com/Sourabh7singh/MongoForge
Project-URL: Issues, https://github.com/Sourabh7singh/MongoForge/issues
Keywords: mongodb,odm,orm,pymongo,django,schema,validation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pymongo>=4.6
Requires-Dist: python-dotenv>=1.0
Requires-Dist: python-slugify>=8.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: mongomock>=4.0; extra == "dev"

# 🔧 MongoForge

A **Django-inspired MongoDB ODM** for Python — define document schemas as classes with typed fields, validate data automatically, and perform CRUD operations through a clean manager API.

Built on [PyMongo](https://pymongo.readthedocs.io/).

---

## ✨ Features

- **Declarative schemas** — define documents as Python classes with typed fields
- **10 built-in field types** — `StringField`, `IntField`, `FloatField`, `BoolField`, `ListField`, `DateTimeField`, `DictField`, `ObjectIdField`, `EmbeddedField` + extensible `BaseField`
- **Rich validation** — required, min/max length, min/max value, regex, choices, custom validators
- **Metaclass magic** — automatic field collection, inheritance, collection naming
- **Manager API** — `create`, `bulk_create`, `all`, `first`, `get`, `filter`, `count`, `update`, `update_one`, `update_many`, `delete_one`, `delete_many`
- **Django-style filter operators** — `age__gte=18`, `name__in=[...]`, `status__ne="inactive"` (supports: `gt`, `gte`, `lt`, `lte`, `ne`, `in`, `nin`)
- **Auto timestamps** — `DateTimeField(auto_now=True)` and `auto_now_add=True`
- **Attribute access** — `user.name` instead of `user["name"]`
- **Serialization** — `to_mongo()` and `from_mongo()` for round-trip conversion
- **Abstract models & inheritance** — share fields across model hierarchies

---

## 📦 Installation

```bash
# Clone the repository
git clone https://github.com/Sourabh7singh/MongoForge
cd MongoForge

# Create and activate a virtual environment
python -m venv env
env\Scripts\activate      # Windows
# source env/bin/activate # macOS/Linux

# Install dependencies
pip install pymongo python-dotenv python-slugify

# For running tests (no live MongoDB needed)
pip install pytest mongomock
```

### Environment Setup

Create a `.env` file in the project root:

```env
MONGO_URI=mongodb://localhost:27017/
DATABASE_NAME=mongoforge
```

---

## 🚀 Quick Start

### Define a Model

```python
from mongoforge import MongoForge
from mongoforge.fields import StringField, IntField, BoolField, DateTimeField, ListField

class User(MongoForge):
    name     = StringField(required=True, min_length=2, max_length=100)
    email    = StringField(required=True, regex=r"^[\w.+-]+@[\w-]+\.[\w.-]+$")
    age      = IntField(min_value=0, max_value=150)
    is_admin = BoolField(default=False)
    tags     = ListField(StringField())
    created  = DateTimeField(auto_now_add=True)
    updated  = DateTimeField(auto_now=True)

    class Meta:
        collection = "users"
```

### Create & Validate

```python
user = User(name="Saurabh", email="saurabh@example.com", age=25)

# Validate before saving
user.validate()  # raises ValidationError if invalid

# Serialize to MongoDB document
doc = user.to_mongo()

# Insert into database
User.objects.create(doc)
```

### Query

```python
# Get all documents
User.objects.all()                        # → list[dict]

# Filter with operators
User.objects.filter(age__gte=18)          # age >= 18
User.objects.filter(name__in=["Alice", "Bob"])

# Get single document
User.objects.get({"email": "saurabh@example.com"})

# Count
User.objects.count()
```

### Update & Delete

```python
# Update by ID
User.objects.update(id="...", update_data={"name": "Updated Name"})

# Update many
User.objects.update_many({"age": {"$gte": 30}}, {"is_admin": True})

# Delete
User.objects.delete_one({"name": "Alice"})
User.objects.delete_many({"age": {"$lt": 18}})
```

### Validation in Action

```python
from mongoforge.exceptions import ValidationError

user = User(name="A", email="not-an-email", age=-5)
try:
    user.validate()
except ValidationError as exc:
    print(exc)
    # Validation failed. name: Minimum length is 2; email: Value does not match regex ...; age: Minimum value is 0
    print(exc.errors)
    # {'name': ['Minimum length is 2'], 'email': ['Value does not match regex ...'], 'age': ['Minimum value is 0']}
```

---

## 📋 Field Types

| Field | Python Type | Extra Options |
|---|---|---|
| `StringField` | `str` | `min_length`, `max_length`, `regex` |
| `IntField` | `int` | `min_value`, `max_value` |
| `FloatField` | `float` | — |
| `BoolField` | `bool` | — |
| `ListField` | `list` | `field` (inner field for item validation) |
| `DateTimeField` | `datetime` | `auto_now`, `auto_now_add` |
| `DictField` | `dict` | — |
| `ObjectIdField` | `ObjectId` | Auto-generates if `None` |
| `EmbeddedField` | `dict` | `document_class` (stub — Phase 4) |

**Common options** (all fields): `required`, `default`, `unique`, `choices`, `validators`, `null`

---

## 🏗️ Architecture

```
Model Class  →  MongoForgeMeta (metaclass)  →  collects fields + attaches MongoManager
                                                          │
                    ┌─────────────────────────────────────┘
                    ▼
              MongoManager (cls.objects)
              ├── CreateMixin   →  create(), bulk_create()
              ├── ReadMixin     →  all(), first(), get(), filter(), count()
              ├── UpdateMixin   →  update(), update_one(), update_many()
              └── DeleteMixin   →  delete_one(), delete_many()
```

---

## 🧪 Running Tests

Tests use [mongomock](https://github.com/mongomock/mongomock) — **no live MongoDB required**.

```bash
# With venv activated
python -m pytest mongoforge/tests/ -v

# Or using venv python directly
env\Scripts\python.exe -m pytest mongoforge/tests/ -v
```

**148 tests** covering:
- All 10 field types + validation paths
- Model instances, attribute access, serialization
- Metaclass, inheritance, abstract models
- Full CRUD operations + filter operators
- Exception hierarchy + formatting

---

## 🗺️ Roadmap

| Phase | Description | Status |
|---|---|---|
| **Phase 1** | Fields & Schema | ✅ ~95% |
| **Phase 2** | QuerySet (chainable queries) | ⬜ Not started |
| **Phase 3** | Model Lifecycle (`save()`, `delete()`, `reload()`, dirty tracking) | 🔶 ~50% |
| **Phase 4** | Relationships (EmbeddedDocument, ReferenceField) | ⬜ Not started |
| **Phase 5** | Indexes, Middleware & CLI | ⬜ Not started |
| **Phase 6** | Packaging & Developer Experience | 🔶 ~30% |

See [implementation_plan.md](implementation_plan.md) for the full roadmap with target APIs.
