美文网首页
使用bulk_create批量创建中发送post_save信号

使用bulk_create批量创建中发送post_save信号

作者: 吾星喵 | 来源:发表于2019-06-11 09:28 被阅读0次

    更多内容请点击 我的博客 查看,欢迎来访。

    关键字:bulk_create无法使用post_save

    要解决的问题:当使用bulk_create批量保存数据时,无法接收post_save信号。

    如何解决:创建Manager,重写bulk_create方法,models中使用该Manager

    批量创建对象

    staff_device = []
    
    for i in range(len(device_number_list)):
        # 数据合并进行处理
        staff_device.append(
            RegisterDevice(
                staff=staff,
                operator=operator
            )
        )
    
    RegisterDevice.objects.bulk_create(staff_device)
    

    这是我的信号

    from django.db.models.signals import post_save, pre_delete
    from django.dispatch import receiver
    from .models import RegisterDevice
    
    
    @receiver(post_save, sender=RegisterDevice)
    def register_device_save_handler(sender, instance=None, created=False, **kwargs):
        if created:
            date = instance.created_time
            operate_mode = instance.operate_mode
            # 创建统计的日期
            count_date = CountDate.objects.get_or_create(date=date)[0]  # (<CountDate: 2019-06-06>, False)  # 如果有为False
    
            count_op_num = CountOpNum.objects.get_or_create(date=count_date, op_mode=operate_mode)[0]
            count_op_num.num += 1
            count_op_num.save()
    

    这个代码主要是用于监听RegisterDevice保存数据的信号,然后将统计数据+1

    现在这个信号不在批量创建中触发。

    查看bulk_create说明和源码

    如前所述,bulk_create不触发这些信号

    bulk-create

    这有几点需要注意:

    • 不会调用模型的save()方法,也不会发送pre_savepost_save信号。
    • 它不适用于多表继承方案中的子模型。
    • 如果模型的主键是AutoField,它不会像save()那样检索和设置主键属性,除非数据库后端支持它(当前是PostgreSQL)。
    • 它不适用于多对多关系。
    • 它将objs转换为一个列表,如果它是一个生成器,它会完全评估objs。 强制转换允许检查所有对象,以便可以首先插入具有手动设置主键的任何对象。

    源码如下:

    # `django\db\models\query.py`源码
    class QuerySet:
        # ............
        def bulk_create(self, objs, batch_size=None):
            """
            Insert each of the instances into the database. Do *not* call
            save() on each of the instances, do not send any pre/post_save
            signals, and do not set the primary key attribute if it is an
            autoincrement field (except if features.can_return_ids_from_bulk_insert=True).
            Multi-table models are not supported.
            """
            # When you bulk insert you don't get the primary keys back (if it's an
            # autoincrement, except if can_return_ids_from_bulk_insert=True), so
            # you can't insert into the child tables which references this. There
            # are two workarounds:
            # 1) This could be implemented if you didn't have an autoincrement pk
            # 2) You could do it by doing O(n) normal inserts into the parent
            #    tables to get the primary keys back and then doing a single bulk
            #    insert into the childmost table.
            # We currently set the primary keys on the objects when using
            # PostgreSQL via the RETURNING ID clause. It should be possible for
            # Oracle as well, but the semantics for extracting the primary keys is
            # trickier so it's not done yet.
            assert batch_size is None or batch_size > 0
            # Check that the parents share the same concrete model with the our
            # model to detect the inheritance pattern ConcreteGrandParent ->
            # MultiTableParent -> ProxyChild. Simply checking self.model._meta.proxy
            # would not identify that case as involving multiple tables.
            for parent in self.model._meta.get_parent_list():
                if parent._meta.concrete_model is not self.model._meta.concrete_model:
                    raise ValueError("Can't bulk create a multi-table inherited model")
            if not objs:
                return objs
            self._for_write = True
            connection = connections[self.db]
            fields = self.model._meta.concrete_fields
            objs = list(objs)
            self._populate_pk_values(objs)
            with transaction.atomic(using=self.db, savepoint=False):
                objs_with_pk, objs_without_pk = partition(lambda o: o.pk is None, objs)
                if objs_with_pk:
                    self._batched_insert(objs_with_pk, fields, batch_size)
                    for obj_with_pk in objs_with_pk:
                        obj_with_pk._state.adding = False
                        obj_with_pk._state.db = self.db
                if objs_without_pk:
                    fields = [f for f in fields if not isinstance(f, AutoField)]
                    ids = self._batched_insert(objs_without_pk, fields, batch_size)
                    if connection.features.can_return_ids_from_bulk_insert:
                        assert len(ids) == len(objs_without_pk)
                    for obj_without_pk, pk in zip(objs_without_pk, ids):
                        obj_without_pk.pk = pk
                        obj_without_pk._state.adding = False
                        obj_without_pk._state.db = self.db
    
            return objs
    

    手动发送信号

    所以必须手动触发它们。如果想让所有模型都这样,可以覆盖bulk_create方法,并在该方法中发送信号

    models.py中创建类-模型管理器

    class BulkCreateManager(models.Manager):
        def bulk_create(self, objs, **kwargs):
            # 发送信号
            from django.db.models.signals import post_save
            for item in objs:
                post_save.send(item.__class__, instance=item, created=True)
    
            return super().bulk_create(objs, **kwargs)
    

    使用模型管理器

    class RegisterDevice(models.Model):
        staff = models.ForeignKey(RegisterStaff, on_delete=models.CASCADE, related_name='devices', verbose_name='使用人')
        # ...........
        created_time = models.DateTimeField(default=timezone.now, verbose_name='创建时间')
    
        objects = BulkCreateManager()  # 需要添加的位置
    
        # 其他代码
    

    相关文章

      网友评论

          本文标题:使用bulk_create批量创建中发送post_save信号

          本文链接:https://www.haomeiwen.com/subject/bncffctx.html