74 lines
3.0 KiB
Python
74 lines
3.0 KiB
Python
|
import sys
|
||
|
from threading import local
|
||
|
|
||
|
from django.contrib.admin.models import LogEntry
|
||
|
from django.db.models.signals import pre_delete, post_save, m2m_changed
|
||
|
from django.dispatch import receiver
|
||
|
from django.utils.decorators import ContextDecorator
|
||
|
|
||
|
from orchestra.utils.python import OrderedSet
|
||
|
|
||
|
from . import manager, Operation, helpers
|
||
|
from .middlewares import OperationsMiddleware
|
||
|
from .models import BackendLog, BackendOperation
|
||
|
|
||
|
|
||
|
@receiver(post_save, dispatch_uid='orchestration.post_save_manager_collector')
|
||
|
def post_save_collector(sender, *args, **kwargs):
|
||
|
if sender not in (BackendLog, BackendOperation, LogEntry):
|
||
|
instance = kwargs.get('instance')
|
||
|
orchestrate.collect(Operation.SAVE, **kwargs)
|
||
|
|
||
|
|
||
|
@receiver(pre_delete, dispatch_uid='orchestration.pre_delete_manager_collector')
|
||
|
def pre_delete_collector(sender, *args, **kwargs):
|
||
|
if sender not in (BackendLog, BackendOperation, LogEntry):
|
||
|
orchestrate.collect(Operation.DELETE, **kwargs)
|
||
|
|
||
|
|
||
|
@receiver(m2m_changed, dispatch_uid='orchestration.m2m_manager_collector')
|
||
|
def m2m_collector(sender, *args, **kwargs):
|
||
|
# m2m relations without intermediary models are shit. Model.post_save is not sent and
|
||
|
# by the time related.post_save is sent rel objects are not accessible via RelatedManager.all()
|
||
|
if kwargs.pop('action') == 'post_add' and kwargs['pk_set']:
|
||
|
orchestrate.collect(Operation.SAVE, **kwargs)
|
||
|
|
||
|
|
||
|
class orchestrate(ContextDecorator):
|
||
|
thread_locals = local()
|
||
|
thread_locals.pending_operations = None
|
||
|
thread_locals.route_cache = None
|
||
|
|
||
|
@classmethod
|
||
|
def collect(cls, action, **kwargs):
|
||
|
""" Collects all pending operations derived from model signals """
|
||
|
if cls.thread_locals.pending_operations is None:
|
||
|
# No active orchestrate context manager
|
||
|
return
|
||
|
kwargs['operations'] = cls.thread_locals.pending_operations
|
||
|
kwargs['route_cache'] = cls.thread_locals.route_cache
|
||
|
instance = kwargs.pop('instance')
|
||
|
manager.collect(instance, action, **kwargs)
|
||
|
|
||
|
def __enter__(self):
|
||
|
cls = type(self)
|
||
|
self.old_pending_operations = cls.thread_locals.pending_operations
|
||
|
cls.thread_locals.pending_operations = OrderedSet()
|
||
|
self.old_route_cache = cls.thread_locals.route_cache
|
||
|
cls.thread_locals.route_cache = {}
|
||
|
|
||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||
|
cls = type(self)
|
||
|
if not exc_type:
|
||
|
operations = cls.thread_locals.pending_operations
|
||
|
if operations:
|
||
|
scripts, serialize = manager.generate(operations)
|
||
|
logs = manager.execute(scripts, serialize=serialize)
|
||
|
for t, msg in helpers.get_messages(logs):
|
||
|
if t == 'error':
|
||
|
sys.stderr.write('%s: %s\n' % (t, msg))
|
||
|
else:
|
||
|
sys.stdout.write('%s: %s\n' % (t, msg))
|
||
|
cls.thread_locals.pending_operations = self.old_pending_operations
|
||
|
cls.thread_locals.route_cache = self.old_route_cache
|