Example #1
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 []
Example #2
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 []