0
# Events & Actions
1
2
Pre/post event hooks for document lifecycle events with action type constants, enabling flexible business logic integration and data validation workflows.
3
4
## Capabilities
5
6
### Event Decorators
7
8
Decorators that register functions to run before or after specific document lifecycle events.
9
10
```python { .api }
11
def before_event(*actions):
12
"""
13
Decorator to register functions that run before document events.
14
15
Args:
16
*actions: Event action types (Insert, Update, Save, Delete, etc.)
17
18
Returns:
19
Decorator function for event handlers
20
"""
21
...
22
23
def after_event(*actions):
24
"""
25
Decorator to register functions that run after document events.
26
27
Args:
28
*actions: Event action types (Insert, Update, Save, Delete, etc.)
29
30
Returns:
31
Decorator function for event handlers
32
"""
33
...
34
```
35
36
### Action Type Constants
37
38
Event type constants that specify which document lifecycle events to handle.
39
40
```python { .api }
41
class Insert:
42
"""Document insertion event type."""
43
...
44
45
class Replace:
46
"""Document replacement event type."""
47
...
48
49
class Save:
50
"""Document save event type (insert or update)."""
51
...
52
53
class SaveChanges:
54
"""Document save changes event type."""
55
...
56
57
class ValidateOnSave:
58
"""Document validation on save event type."""
59
...
60
61
class Delete:
62
"""Document deletion event type."""
63
...
64
65
class Update:
66
"""Document update event type."""
67
...
68
```
69
70
### Action Direction Constants
71
72
Constants that specify timing of event execution relative to the document operation.
73
74
```python { .api }
75
class Before:
76
"""Before event timing constant."""
77
...
78
79
class After:
80
"""After event timing constant."""
81
...
82
```
83
84
## Usage Examples
85
86
### Basic Event Handlers
87
88
```python
89
from beanie import Document, before_event, after_event
90
from beanie import Insert, Update, Save, Delete
91
from datetime import datetime
92
import logging
93
94
class User(Document):
95
name: str
96
email: str
97
created_at: datetime = None
98
updated_at: datetime = None
99
100
class Settings:
101
collection = "users"
102
103
# Before event handlers
104
@before_event(Insert)
105
async def set_creation_time(self):
106
"""Set creation timestamp before insert."""
107
self.created_at = datetime.utcnow()
108
self.updated_at = datetime.utcnow()
109
110
@before_event(Update, Save)
111
async def set_update_time(self):
112
"""Set update timestamp before modifications."""
113
self.updated_at = datetime.utcnow()
114
115
@before_event(Delete)
116
async def log_deletion(self):
117
"""Log before deleting document."""
118
logging.info(f"Deleting user: {self.name} ({self.email})")
119
120
# After event handlers
121
@after_event(Insert)
122
async def welcome_new_user(self):
123
"""Send welcome email after inserting user."""
124
await send_welcome_email(self.email)
125
logging.info(f"New user registered: {self.name}")
126
127
@after_event(Update)
128
async def audit_changes(self):
129
"""Audit user changes after update."""
130
await log_user_changes(self.id, self.get_changes())
131
132
@after_event(Delete)
133
async def cleanup_user_data(self):
134
"""Clean up related data after user deletion."""
135
await cleanup_user_posts(self.id)
136
await cleanup_user_sessions(self.id)
137
```
138
139
### Multiple Event Types
140
141
```python
142
from beanie import Document, before_event, after_event
143
from beanie import Insert, Update, Save, Delete, Replace
144
145
class Product(Document):
146
name: str
147
price: float
148
stock: int
149
active: bool = True
150
151
class Settings:
152
collection = "products"
153
154
# Handle multiple event types
155
@before_event(Insert, Update, Replace, Save)
156
async def validate_product(self):
157
"""Validate product data before any write operation."""
158
if self.price < 0:
159
raise ValueError("Price cannot be negative")
160
if self.stock < 0:
161
raise ValueError("Stock cannot be negative")
162
163
@after_event(Update, Save)
164
async def update_search_index(self):
165
"""Update search index after product changes."""
166
if self.active:
167
await add_to_search_index(self)
168
else:
169
await remove_from_search_index(self.id)
170
171
@after_event(Insert, Update, Replace)
172
async def invalidate_cache(self):
173
"""Invalidate product cache after modifications."""
174
await cache.delete(f"product:{self.id}")
175
await cache.delete("products:all")
176
```
177
178
### Conditional Event Handling
179
180
```python
181
from beanie import Document, before_event, after_event
182
from beanie import Update, Save
183
import asyncio
184
185
class Order(Document):
186
status: str
187
total: float
188
customer_email: str
189
items: List[Dict] = []
190
191
class Settings:
192
collection = "orders"
193
194
@before_event(Update, Save)
195
async def handle_status_change(self):
196
"""Handle order status changes."""
197
# Get previous state if available
198
if hasattr(self, '_previous_status'):
199
old_status = self._previous_status
200
new_status = self.status
201
202
# Only act on status changes
203
if old_status != new_status:
204
if new_status == "shipped":
205
await self.send_shipping_notification()
206
elif new_status == "delivered":
207
await self.send_delivery_confirmation()
208
elif new_status == "cancelled":
209
await self.restore_inventory()
210
211
async def send_shipping_notification(self):
212
"""Send shipping notification email."""
213
await send_email(
214
to=self.customer_email,
215
subject="Your order has shipped!",
216
body=f"Order #{self.id} is on its way."
217
)
218
219
async def send_delivery_confirmation(self):
220
"""Send delivery confirmation."""
221
await send_email(
222
to=self.customer_email,
223
subject="Order delivered",
224
body=f"Order #{self.id} has been delivered."
225
)
226
227
async def restore_inventory(self):
228
"""Restore inventory for cancelled order."""
229
for item in self.items:
230
await restore_item_stock(item['product_id'], item['quantity'])
231
```
232
233
### Event Handler Registration
234
235
```python
236
# Register event handlers on document classes
237
class BlogPost(Document):
238
title: str
239
content: str
240
author_id: str
241
published: bool = False
242
view_count: int = 0
243
244
class Settings:
245
collection = "posts"
246
247
# Method-based event handlers
248
@before_event(Insert)
249
async def generate_slug(self):
250
"""Generate URL slug before insert."""
251
self.slug = self.title.lower().replace(" ", "-")
252
253
@after_event(Update)
254
async def notify_subscribers(self):
255
"""Notify subscribers when post is published."""
256
if self.published and hasattr(self, '_was_draft'):
257
subscribers = await get_subscribers(self.author_id)
258
await notify_post_published(subscribers, self)
259
260
# External event handlers
261
@before_event(Delete)
262
async def archive_post_content(doc: BlogPost):
263
"""Archive post content before deletion."""
264
await archive_content(doc.id, doc.content)
265
266
@after_event(Insert, Update)
267
async def update_author_stats(doc: BlogPost):
268
"""Update author statistics after post changes."""
269
await update_author_post_count(doc.author_id)
270
```
271
272
## Types
273
274
```python { .api }
275
from typing import Callable, Any, Awaitable
276
from enum import Enum
277
278
# Event handler type
279
EventHandler = Callable[[Any], Awaitable[None]]
280
281
# Action type enumeration
282
class ActionType(Enum):
283
INSERT = "INSERT"
284
UPDATE = "UPDATE"
285
REPLACE = "REPLACE"
286
SAVE = "SAVE"
287
SAVE_CHANGES = "SAVE_CHANGES"
288
VALIDATE_ON_SAVE = "VALIDATE_ON_SAVE"
289
DELETE = "DELETE"
290
291
# Direction type enumeration
292
class ActionDirection(Enum):
293
BEFORE = "BEFORE"
294
AFTER = "AFTER"
295
```