Django的“模仿”的批量插入/更新数据库触发器行为/删除(Django “emulate” da

2019-10-20 03:35发布

这是一个自我expaining的问题,但在这里我们去。 我创建Django的业务应用程序,我没有想以“传播”的所有逻辑跨应用程序和数据库,但在另一方面,我不希望让数据库完成这一任务(其可能通过使用触发器 )。

所以我想“复制”的DATABSE触发器的行为,但Model类中在Django(UM目前使用Django 1.4)。

经过一番研究,我想通了,与单个对象,我可以覆盖“models.Model”类的“保存”和“删除”的方法,“之前”和挂钩“之后”将让他们可以前后执行父母的保存/删除。 像这样:

     class MyModel(models.Model):

         def __before(self):
             pass

         def __after(self):
            pass

         @commit_on_success #the decorator is only to ensure that everything occurs inside the same transaction
         def save(self, *args, *kwargs):
             self.__before()
             super(MyModel,self).save(args, kwargs)
             self.__after()

最大的问题是批量操作。 Django不触发运行“更新()” /时,保存/模型的删除“删除()”,从它的查询集。 insted的,它使用的QuerySet自己的方法。 并得到一点点最差的,但它也不触发任何信号。

编辑:只是要更具体一点:视图里面的模型加载是动态的,所以这是不可能的定义“特定模型”的方式。 在这种情况下,我应该创建一个抽象类,并处理它。

我最后的尝试是创建一个自定义管理器,在这个定制经理,重载update方法,循环在查询集里面的模型,trigering“保存()”每个模型的(以考虑上面的实施,或“信号”系统)。 它的工作原理,但结果在数据库中“过载”(想象查询集正在更新10K行)。

Answer 1:

随着几个注意事项,你可以重写查询集的update方法火的信号,同时还使用SQL UPDATE语句:

from django.db.models.signals import pre_save, post_save

def CustomQuerySet(QuerySet):
    @commit_on_success
    def update(self, **kwargs):
        for instance in self:
            pre_save.send(sender=instance.__class__, instance=instance, raw=False, 
                          using=self.db, update_fields=kwargs.keys())
        # use self instead of self.all() if you want to reload all data 
        # from the db for the post_save signal
        result = super(CustomQuerySet, self.all()).update(**kwargs)
        for instance in self:
            post_save.send(sender=instance.__class__, instance=instance, created=False,
                           raw=False, using=self.db, update_fields=kwargs.keys())
        return result

    update.alters_data = True

我克隆当前查询集(使用self.all()因为update的方法将清除QuerySet对象的缓存。

有可能会或可能不会破坏你的代码的几个问题。 首先,它会引入一个竞争条件。 你做的事情pre_save基于数据信号的接收器,当你更新数据库可能不再准确。

也可能有大的查询集一些严重的性能问题。 不像update方法,所有车型必须加载到内存中,然后将信号仍然需要执行。 特别是,如果信号本身必须与数据库交互,性能可能慢得不可接受。 而不像常规pre_save信号,改变模型实例不会自动导致数据库进行更新,因为模型实例不用于保存新的数据。

可能有一些问题,将在几个边缘的情况下会导致问题。

无论如何,如果你能处理这些问题,而无需一些严重的问题,我觉得这是做到这一点的最好办法。 它产生的小的开销,同时尽可能仍然加载模型到内存中,这是非常需要正确地执行各种信号。



Answer 2:

首先,并不是覆盖保存添加__before__after方法,你可以使用内置的pre_savepost_save, pre_delete,post_delete信号。 https://docs.djangoproject.com/en/1.4/topics/signals/

from django.db.models.signals import post_save

class YourModel(models.Model):
    pass

def after_save_your_model(sender, instance, **kwargs):
     pass

# register the signal
post_save.connect(after_save_your_model, sender=YourModel, dispatch_uid=__file__)

pre_deletepost_delete当你调用会得到触发delete()上查询集。

对于批量更新,你将不得不但是手动调用要触发自己的功能。 你可以把它所有的交易为好。

,如果你正在使用动态模型调用适当的触发功能,您可以检查模型的ContentType。 例如:

from django.contrib.contenttypes.models import ContentType

def view(request, app, model_name, method):
    ...
    model = get_model(app, model_name)
    content_type = ContentType.objects.get_for_model(model)
    if content_type == ContenType.objects.get_for_model(YourModel):
        after_save_your_model(model)
    elif content_type == Contentype.objects.get_for_model(AnotherModel):
        another_trigger_function(model)


文章来源: Django “emulate” database trigger behavior on bulk insert/update/delete