Skip to main content

BaseControllerProtocol

Protocol defining the interface for document lifecycle hooks.

Controllers handle business logic and side effects during document
lifecycle events. All hooks receive an optional context parameter
for passing request context without global state.

Lifecycle Hook Order:
Create: validate → before_insert → before_save → [DB INSERT] → after_save → after_insert
Update: validate → before_save → [DB UPDATE] → after_save
Delete: before_delete → [DB DELETE] → after_delete
Submit: validate → before_save → [DB UPDATE] → after_save → on_submit
Cancel: on_cancel → before_save → [DB UPDATE] → after_save

Example - Standard implementation:
class TodoController(BaseController[Todo]):
async def validate(self, context: Any | None = None) -> None:
if not self.doc.title.strip():
raise ValueError("Title cannot be empty")

async def after_insert(self, context: Any | None = None) -> None:
await notify_user(self.doc.owner, "New todo created")

Example - MX custom implementation:
class MongoController:
def __init__(self, doc: MongoDoc) -> None:
self.doc = doc

async def before_insert(self, context: Any | None = None) -> None:
# MongoDB-specific: Generate ObjectId
self.doc._id = ObjectId()

async def validate(self, context: Any | None = None) -> None:
# Custom validation logic
pass

# ... implement other hooks ...

Source: controller.py

Methods

validate

async def validate(self, context: Any | None = None) -> None

Validate document before any save operation.

    Called before insert and update. Raise exceptions for validation errors.

Args:
context: Optional context (user info, request metadata, etc.)

Raises:
ValueError: If validation fails

before_insert

async def before_insert(self, context: Any | None = None) -> None

Hook called before inserting a new document.

    Use for setting defaults, generating values, etc.

Args:
context: Optional context (user info, request metadata, etc.)

after_insert

async def after_insert(self, context: Any | None = None) -> None

Hook called after successfully inserting a new document.

    Use for side effects like notifications, logging, etc.

Args:
context: Optional context (user info, request metadata, etc.)

before_save

async def before_save(self, context: Any | None = None) -> None

Hook called before saving (insert or update).

    Called after validate, before database write.

Args:
context: Optional context (user info, request metadata, etc.)

after_save

async def after_save(self, context: Any | None = None) -> None

Hook called after successfully saving a document.

    Use for cache invalidation, event publishing, etc.

Args:
context: Optional context (user info, request metadata, etc.)

before_delete

async def before_delete(self, context: Any | None = None) -> None

Hook called before deleting a document.

    Use for validation, preventing deletion of linked docs, etc.

Args:
context: Optional context (user info, request metadata, etc.)

after_delete

async def after_delete(self, context: Any | None = None) -> None

Hook called after successfully deleting a document.

    Use for cleanup, cascade operations, etc.

Args:
context: Optional context (user info, request metadata, etc.)

on_submit

async def on_submit(self, context: Any | None = None) -> None

Hook called when a submittable document is submitted.

    Only applies to DocTypes with SubmittableMixin.

Args:
context: Optional context (user info, request metadata, etc.)

on_cancel

async def on_cancel(self, context: Any | None = None) -> None

Hook called when a submittable document is cancelled.

    Only applies to DocTypes with SubmittableMixin.

Args:
context: Optional context (user info, request metadata, etc.)