【问题标题】:How to efficiently fetch objects after created using bulk_create function of Django ORM?使用 Django ORM 的 bulk_create 函数创建后如何有效地获取对象?
【发布时间】:2015-12-27 18:08:43
【问题描述】:

我必须在一个表中插入多个对象,有两种方法可以做到这一点-

1) 使用save() 插入每一个。但在这种情况下,将有 n 个对象的 n 个 sql dB 查询。

2) 使用bulk_create() 将它们全部插入到一起。在这种情况下,对于 n 个对象会有 一个 sql dB 查询。

显然,第二种选择更好,因此我正在使用它。现在bulk__create 的问题是它不返回插入对象的ids,因此它们不能进一步用于创建其他模型的对象,这些模型对所创建的对象具有外键。

为了克服这个问题,我们需要获取 bulk_create 创建的对象。

现在的问题是“假设在我的情况下,没有办法唯一标识创建的对象,我们如何获取它们?”

目前我正在维护一个时间戳来获取它们,如下所示-

my_objects = []

# Timestamp to be used for fetching created objects
time_stamp = datetime.datetime.now()

# Creating list of intantiated objects
for obj_data in obj_data_list:
    my_objects.append(MyModel(**obj_data))

# Bulk inserting the instantiated objects to dB
MyModel.objects.bulk_create(my_objects)

# Using timestamp to fetch the created objects
MyModel.objects.filter(created_at__gte=time_stamp)

现在这很好用,但在一种情况下会失败。

  • 如果在批量创建这些对象时,从其他地方创建了更多对象,那么这些对象也将在我的查询中获取,这是不希望的。

有人可以提出更好的解决方案吗?

【问题讨论】:

    标签: python django database django-models orm


    【解决方案1】:

    由于bulk_create 不会创建主键,您必须自己提供主键。

    如果您不使用默认生成的主键 AutoField,则此过程很简单。

    如果您坚持使用默认值,则需要将代码包装到原子事务中并自己提供主键。这样您就可以知道插入了哪些记录。

    from django.db import transaction
    
    inserted_ids = []
    
    with transacation.atomic():
       my_objects = []
       max_id = int(MyModel.objects.latest('pk').pk)
       id_count = max_id
       for obj_data in obj_data_list:
           id_count += 1
           obj_data['id'] = id_count
           inserted_ids.append(obj_data['id'])
           my_objects.append(MyModel(**obj_data))
       MyModel.objects.bulk_create(my_objects)
       inserted_ids = range(max_id, id_count)
    

    【讨论】:

    • 我赞成您的回答,但是当大量批量时,这种内部处理可能会产生问题?
    • @burhan 如果我使事务原子化,其他对象创建过程会发生什么,他们会等到这个事务完成,还是会被中止?
    • 一个事务保证原子性——这意味着它会阻塞直到完成。
    【解决方案2】:

    你已经知道了。

    如果模型的主键是 AutoField,它不会检索并且 设置主键属性,就像 save() 一样。

    您尝试做的方式,通常是人们做的方式。
    某些情况下的另一种解决方案,这种方式更好。

    my_ids = MyModel.objects.values_list('id', flat=True)
    objs = MyModel.objects.bulk_create(my_objects)
    
    new_objs = MyModel.objects.exclude(id__in=my_ids).values_list('id', flat=True)
    

    【讨论】:

    • 如果您有大量列表要传递,请使用__in,您应该知道这会对性能产生影响。
    • 这就是为什么我用粗体写“一些案例”
    猜你喜欢
    • 2013-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-29
    • 2015-11-17
    • 2012-06-03
    • 1970-01-01
    相关资源
    最近更新 更多