Django的bulk_create()可以外键吗?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django的bulk_create()可以外键吗?相关的知识,希望对你有一定的参考价值。

我需要一次创建很多EVENT对象,然后创建很多ARCHIVED_EVENT对象,这些对象的外键是对应事件的。

我的代码看起来是这样的。

events = []
archivedEvents = []
for _ in range(1000):
    event = Event(name="Test")
    archivedEvent = ArchivedEvent(event_id=event.id)
    archivedEvents.append(archivedEvent)
    events.append(event)

Event.objects.bulk_create(events)
ArchivedEvent.objects.bulk_create(archivedEvents)

不幸的是,所有在这里创建的ARCHIVED_EVENT对象的外键都是NULL。我知道一个对象的主键在保存到数据库之前不会生成。但我在创建归档事件之前保存了事件。我是不是遗漏了什么? 在批量创建归档事件之前,我是否应该刷新缓存?

答案

event.id不会被创建,除非通过 bulk_create(). 也许你可以使用另一个循环来创建 ArchivedEvent. 像这样。

events = []
archivedEvents = []

for _ in range(1000):
    event = Event(name="Test")
    events.append(event)
events = Event.objects.bulk_create(events)

for event in events:
    archivedEvent = ArchivedEvent(event_id=event.id)
    archivedEvents.append(archivedEvent)
ArchivedEvent.objects.bulk_create(archivedEvents)

根据文档,这个解决方案适用于Postgresql。如果你有其他的数据库,那就像这样尝试。

events = []
archivedEvents = []

for _ in range(1000):
    event = Event(name="Test")
    events.append(event)
Event.objects.bulk_create(events)

events = Event.objects.all().order_by('-id')[:1000][::-1]
for event in events:
    archivedEvent = ArchivedEvent(event_id=event.id)
    archivedEvents.append(archivedEvent)
ArchivedEvent.objects.bulk_create(archivedEvents)

为了避免竞赛条件,你可以预先生成主键,然后使用它们。比如说

last_event_id = Event.objects.last().id + 1000 # getting last event id and adding thousand to avoid duplicates.

events = [x+last_event_id for x in range(1000)]
archivedEvents = []

for e in events:
    event = Event(name="Test",pk=e)
    events.append(event)
    archivedEvent = ArchivedEvent(event_id=e)
    archivedEvents.append(archivedEvent)

Event.objects.bulk_create(events)
ArchivedEvent.objects.bulk_create(archivedEvents)
另一答案

下面是一个基于@ruddra的其他数据库解决方案的 避免使用数据库锁的竞赛条件的解决方案。希望能帮到你。

from django.db import transaction

with transaction.atomic():

   # lock first row to avoid race condition
   # note: first row of Event table must have content,
   #       if not, you need use other tables's not empty row to add lock.
   Event.objects.select_for_update().first()

   # just @ruddra's mysql solution
   events = []
   archivedEvents = []

   for _ in range(1000):
       event = Event(name="Test")
       events.append(event)
   Event.objects.bulk_create(events)

   event_ids = Event.objects.values_list('id', flat=True).order_by('-id')[:1000][::-1]
   for event_id in event_ids:
       archivedEvent = ArchivedEvent(event_id=event_id)
       archivedEvents.append(archivedEvent)
   ArchivedEvent.objects.bulk_create(archivedEvents)

以上是关于Django的bulk_create()可以外键吗?的主要内容,如果未能解决你的问题,请参考以下文章

为什么在Django上使用bulk_create插入带有外键的数据会返回“属性对象不可调用”?

django 的 bulk_create 是原子的吗?

Oracle一个字段的的外键可以当另一个字段的主键吗

外键可以引用组合的唯一键吗?

Eloquent 可以替换数据库中的外键吗?

当主键在不同的表中时,可以引用两个外键吗?