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.)