AsyncIO MongoDB Object Document Mapper for Python using type hints
—
ODMantic field definitions provide MongoDB-specific options for model attributes, including indexing, validation, primary keys, and custom MongoDB field names.
Define model fields with MongoDB-specific configuration options.
def Field(
default=...,
*,
key_name=None,
primary_field=False,
index=False,
unique=False,
default_factory=None,
title=None,
description=None,
json_schema_extra=None,
const=None,
gt=None,
ge=None,
lt=None,
le=None,
multiple_of=None,
min_items=None,
max_items=None,
min_length=None,
max_length=None,
regex=None,
examples=None
):
"""
Define a model field with MongoDB-specific options.
Args:
default: Default value for the field (use ... for required fields)
key_name: MongoDB field name (different from Python attribute name)
primary_field: Mark this field as primary key (_id in MongoDB)
index: Create an index on this field
unique: Create a unique index on this field
default_factory: Callable that returns default value
title: Field title for schema
description: Field description for schema
json_schema_extra: Additional JSON schema data
const: Field must have this exact value
gt: Greater than constraint (numbers only)
ge: Greater than or equal constraint (numbers only)
lt: Less than constraint (numbers only)
le: Less than or equal constraint (numbers only)
multiple_of: Must be multiple of this value (numbers only)
min_items: Minimum items in sequence
max_items: Maximum items in sequence
min_length: Minimum string length
max_length: Maximum string length
regex: Regular expression pattern for strings
examples: Example values for schema
Returns:
Field configuration for use in model definition
"""Proxy objects for building queries against model fields.
class FieldProxy:
"""Proxy for model fields enabling query building."""
def __eq__(self, other):
"""Equality comparison for queries."""
def __ne__(self, other):
"""Not equal comparison for queries."""
def __gt__(self, other):
"""Greater than comparison for queries."""
def __ge__(self, other):
"""Greater than or equal comparison for queries."""
def __lt__(self, other):
"""Less than comparison for queries."""
def __le__(self, other):
"""Less than or equal comparison for queries."""from odmantic import Model, Field
from typing import Optional
from datetime import datetime
class User(Model):
# Required field with no default
name: str
# Field with default value
is_active: bool = True
# Field with validation constraints
age: int = Field(ge=0, le=150, description="User age in years")
# Field with unique constraint
email: str = Field(unique=True, description="User email address")
# Field with default factory
created_at: datetime = Field(default_factory=datetime.utcnow)
# Optional field
bio: Optional[str] = Field(None, max_length=500)class Product(Model):
name: str
# Python attribute is 'price_usd' but stored as 'price' in MongoDB
price_usd: float = Field(key_name="price", gt=0)
# Python attribute is 'desc' but stored as 'description' in MongoDB
desc: str = Field(key_name="description", max_length=1000)from odmantic import ObjectId
import uuid
# Default ObjectId primary key (automatic)
class DefaultPrimary(Model):
name: str
# id: ObjectId field is automatically created
# Custom primary key
class CustomPrimary(Model):
custom_id: str = Field(primary_field=True, default_factory=lambda: str(uuid.uuid4()))
name: str
# This model uses custom_id as _id in MongoDB
# ObjectId primary key with custom name
class NamedPrimary(Model):
user_id: ObjectId = Field(primary_field=True, default_factory=ObjectId)
name: strclass SearchableDocument(Model):
title: str = Field(index=True) # Regular index
slug: str = Field(unique=True) # Unique index
content: str
tags: list[str] = Field(index=True) # Index on array field
# Multiple fields with indexes
author: str = Field(index=True)
category: str = Field(index=True)
published_date: datetime = Field(default_factory=datetime.utcnow, index=True)from typing import List
import re
class ValidationExample(Model):
# String validation
username: str = Field(min_length=3, max_length=20, regex=r"^[a-zA-Z0-9_]+$")
# Numeric validation
score: float = Field(ge=0.0, le=100.0, multiple_of=0.1)
rating: int = Field(ge=1, le=5)
# List validation
tags: List[str] = Field(min_items=1, max_items=10)
# Constant value
version: str = Field(const="1.0")
# Example values for documentation
status: str = Field(examples=["active", "inactive", "pending"])from odmantic import Model, Field
from datetime import datetime
from uuid import uuid4
class DefaultExamples(Model):
# Static default
status: str = "pending"
# Default with Field()
priority: int = Field(default=1)
# Factory function for dynamic defaults
created_at: datetime = Field(default_factory=datetime.utcnow)
unique_code: str = Field(default_factory=lambda: str(uuid4()))
# Optional field with None default
notes: Optional[str] = Field(default=None)
# Required field (no default)
name: str = Field(...)from decimal import Decimal
from enum import Enum
class Status(str, Enum):
ACTIVE = "active"
INACTIVE = "inactive"
PENDING = "pending"
class ComplexModel(Model):
# Enum field
status: Status = Field(default=Status.PENDING)
# Decimal field with precision constraints
price: Decimal = Field(gt=0, max_digits=10, decimal_places=2)
# Nested dict with validation
metadata: dict[str, str] = Field(default_factory=dict)
# Complex validation with regex
phone: str = Field(regex=r"^\+?1?\d{9,15}$", description="Phone number in international format")
# List with item constraints
scores: List[int] = Field(default_factory=list, min_items=0, max_items=100)# Fields automatically become FieldProxy objects for queries
class User(Model):
name: str = Field(index=True)
age: int = Field(ge=0)
email: str = Field(unique=True)
# Use fields in queries
from odmantic import AIOEngine
async def query_examples(engine: AIOEngine):
# Equality
users = await engine.find(User, User.name == "Alice")
# Comparison
adults = await engine.find(User, User.age >= 18)
young_adults = await engine.find(User, User.age >= 18, User.age < 30)
# String matching
gmail_users = await engine.find(User, User.email.match(r".*@gmail\.com"))
# Multiple conditions
active_adults = await engine.find(
User,
User.age >= 18,
User.name != "Admin"
)from pydantic import field_validator
class AdvancedModel(Model):
# Field with comprehensive configuration
name: str = Field(
min_length=2,
max_length=50,
description="Full name of the user",
examples=["John Doe", "Jane Smith"],
json_schema_extra={"pattern": "^[A-Za-z ]+$"}
)
# Custom validation with field_validator
email: str = Field(unique=True, description="User email address")
@field_validator('email')
@classmethod
def validate_email(cls, v):
if not v or '@' not in v:
raise ValueError('Invalid email format')
return v.lower().strip()
# Field with custom MongoDB name and validation
account_balance: float = Field(
key_name="balance",
ge=0.0,
description="Account balance in USD",
json_schema_extra={"multipleOf": 0.01}
)Install with Tessl CLI
npx tessl i tessl/pypi-odmantic