您现在的位置是: 网站首页 >Django Django

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

admin2019年6月10日 11:55 Django 1878人已围观

关键字:`bulk_create`无法使用`post_save` 要解决的问题:当使用`bulk_create`批量保存数据时,无法接收`post_save`信号。 如何解决:创建`Manager`,重写`bulk_create`方法,models中使用该`Manager`。 ## 批量创建对象 ```python staff_device = [] for i in range(len(device_number_list)): # 数据合并进行处理 staff_device.append( RegisterDevice( staff=staff, operator=operator ) ) RegisterDevice.objects.bulk_create(staff_device) ``` 这是我的信号 ```python 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](https://docs.djangoproject.com/zh-hans/2.2/ref/models/querysets/#bulk-create) 这有几点需要注意: - 不会调用模型的`save()`方法,也不会发送`pre_save`和`post_save`信号。 - 它不适用于多表继承方案中的子模型。 - 如果模型的主键是`AutoField`,它不会像`save()`那样检索和设置主键属性,除非数据库后端支持它(当前是PostgreSQL)。 - 它不适用于多对多关系。 - 它将`objs`转换为一个列表,如果它是一个生成器,它会完全评估`objs`。 强制转换允许检查所有对象,以便可以首先插入具有手动设置主键的任何对象。 源码如下: ```python # `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`中创建类-模型管理器 ```python 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) ``` 使用模型管理器 ```python 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() # 需要添加的位置 # 其他代码 ```

很赞哦! (1)

文章交流

  • emoji
1人参与,1条评论
好奇一路人 2019年6月20日 21:00
信号使用

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行2084天
  • 系统信息:Linux
  • 后台程序:Python: 3.8.10
  • 网站框架:Django: 3.2.6
  • 文章统计:256 篇
  • 文章评论:60 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 公众号:微信扫描二维码,关注我们
  • QQ群:QQ加群,下载网站的学习源码
返回
顶部
标题 换行 登录
网站