Source code for derrida.books.signals

import logging

from django.db import models
from haystack.exceptions import NotHandled
from haystack.signals import RealtimeSignalProcessor

from derrida.books.models import Instance, Work, Reference
from derrida.outwork.models import Outwork
from derrida.interventions.models import Intervention


logger = logging.getLogger(__name__)


[docs]class RelationSafeRTSP(RealtimeSignalProcessor):
[docs] def handle_save(self, sender, instance, **kwargs): """ This code is entirely adapted from Haystack source with custom handling of the Instance model to cascade updates. Using super did not clarify code in a useful way. Searches gets all backends in use, iterates through them, for all models. Custom handling makes updates to an Instance also trigger an reindex on all of the Reference and Intervention instances associated with it. """ # NOTE: could refine this logic to take advantage of # information in kwargs, including created flag and # update fields # https://docs.djangoproject.com/en/2.0/ref/signals/#post-save using_backends = self.connection_router.for_write(instance=instance) for using in using_backends: uindex = self.connections[using].get_unified_index() try: logger.debug('Indexing %r' % instance) index = uindex.get_index(sender) index.update_object(instance, using=using) except NotHandled: # models without an index configured should be ignored pass # construct a list of additional items that need # to be updated in the index; should be a tuple of # model class and queryset related_updates = [] if sender == Work: # if sender is Work, we need to check for Instances, # References and Interventions and reindex them too. related_updates = [ # all instances associated with current work (Instance, instance.instance_set.all()), # any reference associated with instances of this work (Reference, Reference.objects.filter(instance__work=instance)), # any intervention associated with pages on a digital # edition for an instance of this work (Intervention, Intervention.objects \ .filter(canvas__manifest__instance__work=instance)), ] elif sender == Instance: related_updates = [ (Reference, Reference.objects.filter(instance=instance)), (Intervention, Intervention.objects \ .filter(canvas__manifest__instance=instance)), ] # index any related objects based on current change if related_updates: # this is basically what index.update_object does, # except we are skipping the should_update check # (which defaults to true anyway) for klass, items in related_updates: # if there are any items to index, handle them if items: # NOTE: could use klass._meta.verbose_name.title() # and verbose_name_plural here; class maybe more useful logger.debug('Indexing %d %s%s' % (items.count(), klass.__name__, '' if items.count() == 1 else 's')) index = uindex.get_index(klass) backend = index.get_backend(using) backend.update(index, items)
[docs] def setup(self): # default haystack behavior is to listen to all models # override and only bind to models we index or that affect indexes for model in Work, Instance, Reference, Intervention, Outwork: models.signals.post_save.connect(self.handle_save, sender=model) # (we probably don't care about delete for Work, but ok to leave...) models.signals.post_delete.connect(self.handle_delete, sender=model)
[docs] def teardown(self): # default haystack behavior; does this work to clear model-specific # senders or does it need to match the connect call? # Naive (listen to all model saves). models.signals.post_save.disconnect(self.handle_save) models.signals.post_delete.disconnect(self.handle_delete)