AsyncIO MongoDB Object Document Mapper for Python using type hints
—
ODMantic models define the structure and validation for MongoDB documents using Pydantic. Models provide type safety, data validation, and automatic serialization/deserialization between Python objects and MongoDB documents.
Base class for MongoDB document models with full Pydantic integration.
class Model:
"""Base class for MongoDB document models."""
# Class attributes
__collection__: str # MongoDB collection name (auto-generated from class name)
__config__: ODMConfigDict # Model configuration
def model_copy(self, *, include=None, exclude=None, update=None, deep=False):
"""
Create a copy of the model with optional changes.
Args:
include: Fields to include in the copy
exclude: Fields to exclude from the copy
update: Dictionary of field updates to apply
deep: Whether to make a deep copy
Returns:
New model instance with changes applied
"""
def copy(self, *, include=None, exclude=None, update=None, deep=False):
"""
Create a copy of the model (deprecated, use model_copy).
Args:
include: Fields to include in the copy
exclude: Fields to exclude from the copy
update: Dictionary of field updates to apply
deep: Whether to make a deep copy
Returns:
New model instance with changes applied
"""
def model_update(self, **kwargs):
"""
Update model instance in place with new values.
Args:
**kwargs: Field names and new values
Returns:
Self for method chaining
"""
def update(self, **kwargs):
"""
Update model instance in place (deprecated, use model_update).
Args:
**kwargs: Field names and new values
Returns:
Self for method chaining
"""
def model_dump_doc(self, *, include=None, exclude=None, by_alias=False,
exclude_unset=False, exclude_defaults=False, exclude_none=False):
"""
Convert model to MongoDB document dictionary.
Args:
include: Fields to include in output
exclude: Fields to exclude from output
by_alias: Use field aliases if defined
exclude_unset: Exclude fields that haven't been set
exclude_defaults: Exclude fields set to their default values
exclude_none: Exclude fields set to None
Returns:
dict: MongoDB document representation
"""
def doc(self, include=None):
"""
Convert to MongoDB document (deprecated, use model_dump_doc).
Args:
include: Fields to include in output
Returns:
dict: MongoDB document representation
"""
@classmethod
def model_validate_doc(cls, raw_doc):
"""
Parse MongoDB document into model instance.
Args:
raw_doc: Raw MongoDB document dictionary
Returns:
Model instance parsed from document
Raises:
DocumentParsingError: If parsing fails
"""
@classmethod
def parse_doc(cls, raw_doc):
"""
Parse MongoDB document (deprecated, use model_validate_doc).
Args:
raw_doc: Raw MongoDB document dictionary
Returns:
Model instance parsed from document
"""Base class for embedded documents that can be nested within other models.
class EmbeddedModel:
"""Base class for embedded documents."""
def model_update(self, **kwargs):
"""
Update embedded model instance in place with new values.
Args:
**kwargs: Field names and new values
Returns:
Self for method chaining
"""
def update(self, **kwargs):
"""
Update embedded model instance (deprecated, use model_update).
Args:
**kwargs: Field names and new values
Returns:
Self for method chaining
"""class ODMConfigDict(ConfigDict):
"""Configuration dictionary for ODM models."""
collection: str | None # Custom collection name
parse_doc_with_default_factories: bool # Use default factories when parsing
indexes: Callable[[], Iterable[Index | IndexModel]] | None # Additional indexes
# Pydantic options (selected subset)
title: str | None
json_schema_extra: dict | None
str_strip_whitespace: bool
arbitrary_types_allowed: bool
extra: Literal["allow", "forbid", "ignore"] | Nonefrom odmantic import Model, Field
from typing import Optional
from datetime import datetime
class User(Model):
name: str
email: str = Field(unique=True)
age: int = Field(ge=0, le=150)
created_at: datetime = Field(default_factory=datetime.utcnow)
is_active: bool = True
tags: Optional[list[str]] = None
# Custom collection name (optional)
class Config:
collection = "users"from odmantic import Model
from odmantic.config import ODMConfigDict
class Product(Model):
name: str
price: float = Field(gt=0)
description: Optional[str] = None
model_config = ODMConfigDict(
collection="products",
extra="forbid" # Don't allow extra fields
)from odmantic import Model, EmbeddedModel
class Address(EmbeddedModel):
street: str
city: str
country: str
zip_code: str
class Person(Model):
name: str
address: Address
work_address: Optional[Address] = None
# Usage
address = Address(
street="123 Main St",
city="New York",
country="USA",
zip_code="10001"
)
person = Person(name="John Doe", address=address)# Create instance
user = User(
name="Alice Smith",
email="alice@example.com",
age=30,
tags=["developer", "python"]
)
# Copy with changes
updated_user = user.model_copy(update={"age": 31})
# Update in place
user.model_update(age=32, is_active=False)
# Convert to MongoDB document
doc = user.model_dump_doc()
print(doc) # {'_id': ObjectId(...), 'name': 'Alice Smith', ...}
# Parse from MongoDB document
raw_doc = {
'_id': ObjectId('...'),
'name': 'Bob Jones',
'email': 'bob@example.com',
'age': 25,
'created_at': datetime.utcnow(),
'is_active': True
}
user = User.model_validate_doc(raw_doc)from odmantic import Model, Reference, ObjectId
class Author(Model):
name: str
email: str
class Book(Model):
title: str
author_id: ObjectId = Reference()
published_year: int
# You can define a property to load the related author
# (This would require custom implementation with the engine)
# Usage
author = Author(name="Jane Doe", email="jane@example.com")
book = Book(
title="Python Patterns",
author_id=author.id,
published_year=2023
)from pydantic import validator, field_validator
class User(Model):
name: str
email: str
age: int
@field_validator('email')
@classmethod
def validate_email(cls, v):
if '@' not in v:
raise ValueError('Invalid email format')
return v.lower()
@field_validator('name')
@classmethod
def validate_name(cls, v):
if len(v.strip()) < 2:
raise ValueError('Name must be at least 2 characters')
return v.strip().title()
# This will raise validation errors for invalid data
try:
user = User(name="A", email="invalid", age=25)
except ValidationError as e:
print(e.errors())from odmantic import Model, Field
import uuid
class CustomIdModel(Model):
custom_id: str = Field(primary_field=True, default_factory=lambda: str(uuid.uuid4()))
name: str
value: int
# The custom_id field will be stored as _id in MongoDB
model = CustomIdModel(name="test", value=42)
print(model.custom_id) # Auto-generated UUID stringfrom odmantic import Model
from odmantic.config import ODMConfigDict
class ConfiguredModel(Model):
name: str
secret_field: str
model_config = ODMConfigDict(
# MongoDB collection name
collection="my_collection",
# Extra fields behavior: "allow", "forbid", "ignore"
extra="forbid",
# Validate field assignments
validate_assignment=True,
# Use enum values instead of names
use_enum_values=True,
# Allow population by field name and alias
populate_by_name=True
)Install with Tessl CLI
npx tessl i tessl/pypi-odmantic