Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
    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),
        ]
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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 []
Ejemplo n.º 7
0
    def get_triggers(self):

        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).id)

        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.db_table,
                    (content_type,
                        self.this_model._meta.pk.get_attname_column()[1]),
                    **{self.field.get_attname_column()[1]:"NEW.%s" % 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.db_table,
                    (content_type,
                        self.this_model._meta.pk.get_attname_column()[1]),
                    **{self.field.get_attname_column()[1]:"OLD.%s" % self.other_model._meta.pk.get_attname_column()[1]}
                )
            )
            return [
                triggers.Trigger(self.other_model,"after","update",[action_new],content_type),
                triggers.Trigger(self.other_model,"after","insert",[action_new],content_type),
                triggers.Trigger(self.other_model,"after","delete",[action_old],content_type),
            ]

        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 = (
                    content_type,
                    "NEW.%s" % self.field.get_attname_column()[1],
                )
            )
            action_old = triggers.TriggerActionInsert(
                model = DirtyInstance,
                columns = ("content_type_id","object_id"),
                values = (
                    content_type,
                    "OLD.%s" % self.field.get_attname_column()[1],
                )
            )
            return [
                triggers.Trigger(self.other_model,"after","update",[action_new,action_old],content_type),
                triggers.Trigger(self.other_model,"after","insert",[action_new],content_type),
                triggers.Trigger(self.other_model,"after","delete",[action_old],content_type),
            ]

        if "m2m" in self.type:
            # The two directions of M2M relations only differ in the column
            # names used in the intermediate table.
            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()

            # 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,
                )
            )

            # 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.
            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.id"}
                )
            )

            return [
                triggers.Trigger(self.field,"after","update",[action_m2m_new,action_m2m_old],content_type),
                triggers.Trigger(self.field,"after","insert",[action_m2m_new],content_type),
                triggers.Trigger(self.field,"after","delete",[action_m2m_old],content_type),
                triggers.Trigger(self.other_model,"after","update",[action_new],content_type),
            ]

        return []