from threading import local from django.db.models.signals import pre_delete, pre_save from django.dispatch import receiver from django.http.response import HttpResponseServerError from orchestra.core import services from orchestra.utils.python import OrderedSet from .models import Order @receiver(pre_save, dispatch_uid='orders.ppre_save_collector') def pre_save_collector(sender, *args, **kwargs): if sender in services: OrderMiddleware.collect(Order.SAVE, **kwargs) @receiver(pre_delete, dispatch_uid='orders.pre_delete_collector') def pre_delete_collector(sender, *args, **kwargs): if sender in services: OrderMiddleware.collect(Order.DELETE, **kwargs) class OrderCandidate(object): def __unicode__(self): return "{}.{}()".format(str(self.instance), self.action) def __init__(self, instance, action): self.instance = instance self.action = action def __hash__(self): """ set() """ opts = self.instance._meta model = opts.app_label + opts.model_name return hash(model + str(self.instance.pk) + self.action) def __eq__(self, candidate): """ set() """ return hash(self) == hash(candidate) class OrderMiddleware(object): """ Stores all the operations derived from save and delete signals and executes them at the end of the request/response cycle """ # Thread local is used because request object is not available on model signals thread_locals = local() @classmethod def get_order_candidates(cls): # Check if an error poped up before OrdersMiddleware.process_request() if hasattr(cls.thread_locals, 'request'): request = cls.thread_locals.request if not hasattr(request, 'order_candidates'): request.order_candidates = OrderedSet() return request.order_candidates return set() @classmethod def collect(cls, action, **kwargs): """ Collects all pending operations derived from model signals """ request = getattr(cls.thread_locals, 'request', None) if request is None: return order_candidates = cls.get_order_candidates() instance = kwargs['instance'] order_candidates.add(OrderCandidate(instance, action)) def process_request(self, request): """ Store request on a thread local variable """ type(self).thread_locals.request = request def process_response(self, request, response): if not isinstance(response, HttpResponseServerError): candidates = type(self).get_order_candidates() Order.process_candidates(candidates) return response