def m2m_triggers(self, content_type, fk_name, related_field, using): """ Returns triggers for m2m relation """ related_inc_where, _ = self.get_related_where(fk_name, using, 'NEW') related_dec_where, related_where_params = self.get_related_where( fk_name, using, 'OLD') related_increment = triggers.TriggerActionUpdate( model=self.model, columns=(self.fieldname, ), values=(self.get_related_increment_value(using), ), where=(' AND '.join(related_inc_where), related_where_params), ) related_decrement = triggers.TriggerActionUpdate( model=self.model, columns=(self.fieldname, ), values=(self.get_related_decrement_value(using), ), where=(' AND '.join(related_dec_where), related_where_params), ) trigger_list = [ triggers.Trigger(related_field, "after", "update", [related_increment, related_decrement], content_type, using, self.skip), triggers.Trigger(related_field, "after", "insert", [related_increment], content_type, using, self.skip), triggers.Trigger(related_field, "after", "delete", [related_decrement], content_type, using, self.skip), ] return trigger_list
def get_triggers(self): fk_name = self.manager.related.field.attname content_type = str(ContentType.objects.get_for_model(self.model).pk) # create the triggers for the incremental updates increment = triggers.TriggerActionUpdate( model=self.model, columns=(self.fieldname, ), values=("%s+1" % self.fieldname, ), where="%s=NEW.%s" % (self.model._meta.pk.get_attname_column()[1], fk_name), ) decrement = triggers.TriggerActionUpdate( model=self.model, columns=(self.fieldname, ), values=("%s-1" % self.fieldname, ), where="%s=OLD.%s" % (self.model._meta.pk.get_attname_column()[1], fk_name), ) other_model = self.manager.related.model return [ triggers.Trigger(other_model, "after", "update", [increment, decrement], content_type), triggers.Trigger(other_model, "after", "insert", [increment], content_type), triggers.Trigger(other_model, "after", "delete", [decrement], content_type), ]
def get_triggers(self, using): qn = self.get_quote_name(using) content_type = str(ContentType.objects.get_for_model(self.model).pk) # 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 action = triggers.TriggerActionUpdate( model=self.model, columns=(self.fieldname, ), values=(triggers.RandomBigInt(), ), where="%s = NEW.%s" % ((qn(self.model._meta.pk.get_attname_column()[1]), ) * 2), ) 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(CacheKeyDenorm, self).get_triggers(using=using)
def get_triggers(self, using): if using: cconnection = connections[using] else: cconnection = connection qn = self.get_quote_name(using) related_field = self.manager.related.field if isinstance(related_field, ManyToManyField): fk_name = related_field.m2m_reverse_name() inc_where = [ "%(id)s IN (SELECT %(reverse_related)s FROM %(m2m_table)s WHERE %(related)s = NEW.%(id)s)" % { 'id': qn(self.model._meta.pk.get_attname_column()[0]), 'related': qn(related_field.m2m_column_name()), 'm2m_table': qn(related_field.m2m_db_table()), 'reverse_related': qn(fk_name), } ] dec_where = [ action.replace('NEW.', 'OLD.') for action in inc_where ] else: pk_name = qn(self.model._meta.pk.get_attname_column()[1]) fk_name = qn(related_field.attname) inc_where = ["%s = NEW.%s" % (pk_name, fk_name)] dec_where = ["%s = OLD.%s" % (pk_name, fk_name)] content_type = str(ContentType.objects.get_for_model(self.model).pk) inc_query = TriggerFilterQuery(self.manager.related.model, trigger_alias='NEW') inc_query.add_q(Q(**self.filter)) inc_query.add_q(~Q(**self.exclude)) inc_filter_where, _ = inc_query.where.as_sql( SQLCompiler(inc_query, cconnection, using).quote_name_unless_alias, cconnection) dec_query = TriggerFilterQuery(self.manager.related.model, trigger_alias='OLD') dec_query.add_q(Q(**self.filter)) dec_query.add_q(~Q(**self.exclude)) dec_filter_where, where_params = dec_query.where.as_sql( SQLCompiler(dec_query, cconnection, using).quote_name_unless_alias, cconnection) if inc_filter_where: inc_where.append(inc_filter_where) if dec_filter_where: dec_where.append(dec_filter_where) # create the triggers for the incremental updates increment = triggers.TriggerActionUpdate( model=self.model, columns=(self.fieldname, ), values=(self.get_increment_value(using), ), where=(' AND '.join(inc_where), where_params), ) decrement = triggers.TriggerActionUpdate( model=self.model, columns=(self.fieldname, ), values=(self.get_decrement_value(using), ), where=(' AND '.join(dec_where), where_params), ) other_model = self.manager.related.model trigger_list = [ triggers.Trigger(other_model, "after", "update", [increment, decrement], content_type, using, self.skip), triggers.Trigger(other_model, "after", "insert", [increment], content_type, using, self.skip), triggers.Trigger(other_model, "after", "delete", [decrement], content_type, using, self.skip), ] if isinstance(related_field, ManyToManyField): trigger_list.extend( self.m2m_triggers(content_type, fk_name, related_field, using)) return trigger_list
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`` action_new = triggers.TriggerActionUpdate( model=self.this_model, columns=(self.fieldname, ), values=(triggers.RandomBigInt(), ), where="%s = NEW.%s" % ( qn(self.field.get_attname_column()[1]), qn(self.other_model._meta.pk.get_attname_column()[1]), ), ) action_old = triggers.TriggerActionUpdate( model=self.this_model, columns=(self.fieldname, ), values=(triggers.RandomBigInt(), ), where="%s = OLD.%s" % ( qn(self.field.get_attname_column()[1]), 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.TriggerActionUpdate( model=self.this_model, columns=(self.fieldname, ), values=(triggers.RandomBigInt(), ), where="%s = NEW.%s" % ( qn(self.this_model._meta.pk.get_attname_column()[1]), qn(self.field.get_attname_column()[1]), ), ) action_old = triggers.TriggerActionUpdate( model=self.this_model, columns=(self.fieldname, ), values=(triggers.RandomBigInt(), ), where="%s = OLD.%s" % ( qn(self.this_model._meta.pk.get_attname_column()[1]), qn(self.field.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 = self.field.m2m_column_name() reverse_column_name = self.field.m2m_reverse_name() if "backward" in self.type: column_name = self.field.m2m_reverse_name() reverse_column_name = self.field.m2m_column_name() else: if "forward" in self.type: column_name = self.field.object_id_field_name reverse_column_name = self.field.rel.to._meta.pk.column if "backward" in self.type: column_name = 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.TriggerActionUpdate( model=self.this_model, columns=(self.fieldname, ), values=(triggers.RandomBigInt(), ), where="%s = NEW.%s" % ( qn(self.this_model._meta.pk.get_attname_column()[1]), qn(column_name), ), ) action_m2m_old = triggers.TriggerActionUpdate( model=self.this_model, columns=(self.fieldname, ), values=(triggers.RandomBigInt(), ), where="%s = OLD.%s" % ( qn(self.this_model._meta.pk.get_attname_column()[1]), qn(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. sql, params = triggers.TriggerNestedSelect( self.field.m2m_db_table(), (column_name, ), **{ reverse_column_name: 'NEW.%s' % qn(self.other_model._meta.pk.get_attname_column()[1]) }).sql() action_new = triggers.TriggerActionUpdate( model=self.this_model, columns=(self.fieldname, ), values=(triggers.RandomBigInt(), ), where=(self.this_model._meta.pk.get_attname_column()[1] + ' IN (' + sql + ')', params), ) trigger_list.append( triggers.Trigger(self.other_model, "after", "update", [action_new], content_type, using, self.skip)) return trigger_list return []