更多内容请点击 我的博客 查看,欢迎来访。
关键字: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
不触发这些信号
这有几点需要注意:
- 不会调用模型的
save()
方法,也不会发送pre_save
和post_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() # 需要添加的位置
# 其他代码
网友评论