def get_triggers(self, using): qn = self.get_quote_name(using) content_type = str(ContentType.objects.get_for_model(self.model).pk) # Create a trigger that marks any updated or newly created # instance of the model containing the denormalized field # as dirty. # This is only really needed if the instance was changed without # using the ORM or if it was part of a bulk update. # In those cases the self_save_handler won't get called by the # pre_save signal, so we need to ensure flush() does this later. action = triggers.TriggerActionInsert( model=DirtyInstance, columns=("content_type_id", "object_id"), values=(content_type, "NEW.%s" % qn(self.model._meta.pk.get_attname_column()[1]))) trigger_list = [ triggers.Trigger(self.model, "after", "update", [action], content_type, using, self.skip), triggers.Trigger(self.model, "after", "insert", [action], content_type, using, self.skip), ] return trigger_list + super(CallbackDenorm, self).get_triggers(using=using)
def get_triggers(self, using): qn = self.get_quote_name(using) if not self.type: # 'resolved_model' model never got called... raise ValueError( "The model '%s' could not be resolved, it probably does not exist" % self.other_model) content_type = str( ContentType.objects.get_for_model(self.this_model).pk) if self.type == "forward": # With forward relations many instances of ``this_model`` # may be related to one instance of ``other_model`` # so we need to do a nested select query in the trigger # to find them all. action_new = triggers.TriggerActionInsert( model=DirtyInstance, columns=("content_type_id", "object_id"), values=triggers.TriggerNestedSelect( self.this_model._meta.pk.model._meta.db_table, (content_type, self.this_model._meta.pk.get_attname_column()[1]), **{ self.field.get_attname_column()[1]: "NEW.%s" % qn(self.other_model._meta.pk.get_attname_column()[1]) })) action_old = triggers.TriggerActionInsert( model=DirtyInstance, columns=("content_type_id", "object_id"), values=triggers.TriggerNestedSelect( self.this_model._meta.pk.model._meta.db_table, (content_type, self.this_model._meta.pk.get_attname_column()[1]), **{ self.field.get_attname_column()[1]: "OLD.%s" % qn(self.other_model._meta.pk.get_attname_column()[1]) })) return [ triggers.Trigger(self.other_model, "after", "update", [action_new], content_type, using, self.skip), triggers.Trigger(self.other_model, "after", "insert", [action_new], content_type, using, self.skip), triggers.Trigger(self.other_model, "after", "delete", [action_old], content_type, using, self.skip), ] if self.type == "backward": # With backward relations a change in ``other_model`` can affect # only one or two instances of ``this_model``. # If the ``other_model`` instance changes the value its ForeignKey # pointing to ``this_model`` both the old and the new related instance # are affected, otherwise only the one it is pointing to is affected. action_new = triggers.TriggerActionInsert( model=DirtyInstance, columns=("content_type_id", "object_id"), values=triggers.TriggerNestedSelect( self.field.model._meta.db_table, (content_type, self.field.get_attname_column()[1]), **{ self.field.model._meta.pk.get_attname_column()[1]: "NEW.%s" % qn(self.other_model._meta.pk.get_attname_column()[1]) })) action_old = triggers.TriggerActionInsert( model=DirtyInstance, columns=("content_type_id", "object_id"), values=triggers.TriggerNestedSelect( self.field.model._meta.db_table, (content_type, self.field.get_attname_column()[1]), **{ self.field.model._meta.pk.get_attname_column()[1]: "OLD.%s" % qn(self.other_model._meta.pk.get_attname_column()[1]) })) return [ triggers.Trigger(self.other_model, "after", "update", [action_new, action_old], content_type, using, self.skip), triggers.Trigger(self.other_model, "after", "insert", [action_new], content_type, using, self.skip), triggers.Trigger(self.other_model, "after", "delete", [action_old], content_type, using, self.skip), ] if "m2m" in self.type: # The two directions of M2M relations only differ in the column # names used in the intermediate table. if isinstance(self.field, models.ManyToManyField): if "forward" in self.type: column_name = qn(self.field.m2m_column_name()) reverse_column_name = self.field.m2m_reverse_name() if "backward" in self.type: column_name = qn(self.field.m2m_reverse_name()) reverse_column_name = self.field.m2m_column_name() else: if "forward" in self.type: column_name = qn(self.field.object_id_field_name) reverse_column_name = self.field.rel.to._meta.pk.column if "backward" in self.type: column_name = qn(self.field.rel.to._meta.pk.column) reverse_column_name = self.field.object_id_field_name # The first part of a M2M dependency is exactly like a backward # ForeignKey dependency. ``this_model`` is backward FK related # to the intermediate table. action_m2m_new = triggers.TriggerActionInsert( model=DirtyInstance, columns=("content_type_id", "object_id"), values=( content_type, "NEW.%s" % column_name, )) action_m2m_old = triggers.TriggerActionInsert( model=DirtyInstance, columns=("content_type_id", "object_id"), values=( content_type, "OLD.%s" % column_name, )) trigger_list = [ triggers.Trigger(self.field, "after", "update", [action_m2m_new, action_m2m_old], content_type, using, self.skip), triggers.Trigger(self.field, "after", "insert", [action_m2m_new], content_type, using, self.skip), triggers.Trigger(self.field, "after", "delete", [action_m2m_old], content_type, using, self.skip), ] if isinstance(self.field, models.ManyToManyField): # Additionally to the dependency on the intermediate table # ``this_model`` is dependant on updates to the ``other_model``- # There is no need to track insert or delete events here, # because a relation can only be created or deleted by # by modifying the intermediate table. # # Generic relations are excluded because they have the # same m2m_table and model table. action_new = triggers.TriggerActionInsert( model=DirtyInstance, columns=("content_type_id", "object_id"), values=triggers.TriggerNestedSelect( self.field.m2m_db_table(), (content_type, column_name), **{ reverse_column_name: 'NEW.%s' % qn(self.other_model._meta.pk.get_attname_column() [1]) })) trigger_list.append( triggers.Trigger(self.other_model, "after", "update", [action_new], content_type, using, self.skip)) return trigger_list return []