Signals API Reference

This module provides signal support for model lifecycle events, allowing you to hook into various stages of document operations.

Available Signals

Document Lifecycle Signals

surrealengine.signals.pre_init Signal sent before document initialization
surrealengine.signals.post_init Signal sent after document initialization
surrealengine.signals.pre_save Signal sent before document save
surrealengine.signals.pre_save_post_validation Signal sent after validation but before save
surrealengine.signals.post_save Signal sent after document save
surrealengine.signals.pre_delete Signal sent before document deletion
surrealengine.signals.post_delete Signal sent after document deletion

Bulk Operation Signals

surrealengine.signals.pre_bulk_insert Signal sent before bulk insert operation
surrealengine.signals.post_bulk_insert Signal sent after bulk insert operation

Field Operation Signals

surrealengine.signals.pre_validate Signal sent before field validation
surrealengine.signals.post_validate Signal sent after field validation
surrealengine.signals.pre_to_db Signal sent before converting value to database format
surrealengine.signals.post_to_db Signal sent after converting value to database format
surrealengine.signals.pre_from_db Signal sent before converting value from database format
surrealengine.signals.post_from_db Signal sent after converting value from database format

Signal Constants

surrealengine.signals.SIGNAL_SUPPORT Boolean indicating if signal support is available

bool(x) -> bool

Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.

Usage Examples

Connecting to Document Signals

from surrealengine.signals import pre_save, post_save
from surrealengine import Document, StringField
import datetime

class User(Document):
    name = StringField(required=True)
    created_at = DateTimeField()
    updated_at = DateTimeField()

# Signal handlers
def set_timestamps(sender, document, **kwargs):
    now = datetime.datetime.utcnow()
    if not document.created_at:
        document.created_at = now
    document.updated_at = now

def log_user_save(sender, document, created, **kwargs):
    action = "created" if created else "updated"
    print(f"User {document.name} was {action}")

# Connect signals
pre_save.connect(set_timestamps, sender=User)
post_save.connect(log_user_save, sender=User)

Audit Trail Example

from surrealengine.signals import post_save, post_delete

class AuditLog(Document):
    action = StringField(required=True)
    model_name = StringField(required=True)
    object_id = StringField()
    timestamp = DateTimeField(auto_now=True)
    user_id = StringField()

def create_audit_log(sender, document, created=None, **kwargs):
    action = "created" if created else "updated"
    AuditLog(
        action=action,
        model_name=sender.__name__,
        object_id=str(document.id),
        user_id=getattr(document, 'user_id', None)
    ).save()

def delete_audit_log(sender, document, **kwargs):
    AuditLog(
        action="deleted",
        model_name=sender.__name__,
        object_id=str(document.id)
    ).save()

# Connect to all document classes
post_save.connect(create_audit_log)
post_delete.connect(delete_audit_log)

Cache Invalidation

from surrealengine.signals import post_save, post_delete
import redis

cache = redis.Redis()

def invalidate_cache(sender, document, **kwargs):
    # Invalidate specific object cache
    cache_key = f"{sender.__name__.lower()}:{document.id}"
    cache.delete(cache_key)

    # Invalidate list caches
    list_key = f"{sender.__name__.lower()}:list:*"
    for key in cache.scan_iter(match=list_key):
        cache.delete(key)

# Connect to multiple models
for model in [User, Post, Comment]:
    post_save.connect(invalidate_cache, sender=model)
    post_delete.connect(invalidate_cache, sender=model)

Custom Validation

from surrealengine.signals import pre_save
from surrealengine.exceptions import ValidationError

def validate_user_email_domain(sender, document, **kwargs):
    if hasattr(document, 'email') and document.email:
        allowed_domains = ['company.com', 'partner.com']
        domain = document.email.split('@')[1]
        if domain not in allowed_domains:
            raise ValidationError(f"Email domain {domain} not allowed")

pre_save.connect(validate_user_email_domain, sender=User)

Conditional Signal Connections

from surrealengine.signals import post_save
import os

def send_welcome_email(sender, document, created, **kwargs):
    if created and hasattr(document, 'email'):
        # Send welcome email logic here
        print(f"Sending welcome email to {document.email}")

# Only connect in production
if os.environ.get('ENV') == 'production':
    post_save.connect(send_welcome_email, sender=User)

Signal Disconnection

from surrealengine.signals import post_save

# Disconnect specific handler
post_save.disconnect(log_user_save, sender=User)

# Disconnect all handlers for a sender
post_save.disconnect(sender=User)

# Disconnect handler from all senders
post_save.disconnect(log_user_save)