有没有一种方法可以获取使用django 1.4+中的bulk_create功能创建的项目的主键?
有没有一种方法可以获取使用django 1.4+中的bulk_create功能创建的项目的主键?
Answers:
从Django 1.10开始-现在受支持(仅在Postgres上),这是doc的链接。
>>> list_of_objects = Entry.objects.bulk_create([
... Entry(headline="Django 2.0 Released"),
... Entry(headline="Django 2.1 Announced"),
... Entry(headline="Breaking: Django is awesome")
... ])
>>> list_of_objects[0].id
1
从更改日志中:
在Django 1.10中进行了更改:支持在使用PostgreSQL时在使用bulk_create()创建的对象上设置主键
bulk_create
方法返回的列表与您提供的列表相同,并且本地对象(该列表的成员)未设置它,因为pyriku在他的答案中演示了这一点。
根据文档,您无法执行此操作:https : //docs.djangoproject.com/en/dev/ref/models/querysets/#bulk-create
批量创建仅用于此目的:以高效的方式创建许多对象,从而节省大量查询。但这意味着您得到的响应是不完整的。如果您这样做:
>>> categories = Category.objects.bulk_create([
Category(titel="Python", user=user),
Category(titel="Django", user=user),
Category(titel="HTML5", user=user),
])
>>> [x.pk for x in categories]
[None, None, None]
这并不意味着您的类别没有pk,只是查询没有检索到它们(如果键是AutoField
)。如果出于某些原因想要pk,则需要以经典方式保存对象。
bulk_create
,以便可靠地检索创建的ID?
我可以想到两种方法:
a)你可以做
category_ids = Category.objects.values_list('id', flat=True)
categories = Category.objects.bulk_create([
Category(title="title1", user=user, created_at=now),
Category(title="title2", user=user, created_at=now),
Category(title="title3", user=user, created_at=now),
])
new_categories_ids = Category.objects.exclude(id__in=category_ids).values_list('id', flat=True)
如果查询集非常庞大,这可能会有点昂贵。
b)如果模型有一个created_at
字段,
now = datetime.datetime.now()
categories = Category.objects.bulk_create([
Category(title="title1", user=user, created_at=now),
Category(title="title2", user=user, created_at=now),
Category(title="title3", user=user, created_at=now),
])
new_cats = Category.objects.filter(created_at >= now).values_list('id', flat=True)
这有一个限制,即必须有一个字段来存储创建对象的时间。
date_created
字段,因此尽管可以轻松添加一个字段,但是这仍然可以工作。我唯一关心的是,多个查询可能同时命中数据库,因此我想我需要bulk_create
在created_at
查询之前和之后实施某种锁定机制。
select max(id) is better
max(id)
,我尝试了它并遇到了问题。MariaDB文档明确声明,除了唯一性之外,不要承担与PK有关的任何其他事情。
实际上,我的同事已经提出了以下解决方案,现在看来非常明显。添加一个名为的新列bulk_ref
,您将在其中填充一个唯一值并为每一行插入。然后,只需使用bulk_ref
预先设置的查询表,瞧,就可以检索您插入的记录。例如:
cars = [Car(
model="Ford",
color="Blue",
price="5000",
bulk_ref=5,
),Car(
model="Honda",
color="Silver",
price="6000",
bulk_ref=5,
)]
Car.objects.bulk_create(cars)
qs = Car.objects.filter(bulk_ref=5)
# datatime.py
# my datatime function
def getTimeStamp(needFormat=0, formatMS=True):
if needFormat != 0:
return datetime.datetime.now().strftime(f'%Y-%m-%d %H:%M:%S{r".%f" if formatMS else ""}')
else:
ft = time.time()
return (ft if formatMS else int(ft))
def getTimeStampString():
return str(getTimeStamp()).replace('.', '')
# model
bulk_marker = models.CharField(max_length=32, blank=True, null=True, verbose_name='bulk_marker', help_text='ONLYFOR_bulkCreate')
# views
import .........getTimeStampString
data_list(
Category(title="title1", bulk_marker=getTimeStampString()),
...
)
# bulk_create
Category.objects.bulk_create(data_list)
# Get primary Key id
Category.objects.filter(bulk_marker=bulk_marker).values_list('id', flat=True)
在Django文档目前正在限制规定:
如果模型的主键是AutoField,则它不会像那样检索和设置主键属性
save()
。
但是,有个好消息。有几张关于bulk_create
记忆的话题。上面列出的票证最有可能会很快实施解决方案,但是显然不能保证准时或是否会成功。
因此,有两种可能的解决方案,
等待,看看此补丁是否可以投入生产。您可以通过测试规定的解决方案并让django社区了解您的想法/问题来为您提供帮助。https://code.djangoproject.com/attachment/ticket/19527/bulk_create_and_create_schema_django_v1.5.1.patch
覆盖/编写您自己的批量插入解决方案。
这在股票Django中不起作用,但是Django Bug跟踪器中有一个补丁,该补丁使bulk_create设置了所创建对象的主键。
当bulk_create
与结合使用时,@ Or Duan建议的方法适用于PostgreSQL ignore_conflicts=False
。当ignore_conflicts=True
设置,那么你没有得到的值AutoField
(通常是ID)在返回的对象。
这应该工作。
categories = Category.objects.bulk_create([
Category(titel="Python", user=user),
Category(titel="Django", user=user),
Category(titel="HTML5", user=user),
])
>>> categories[0]
[<Category: Python>]
>>> categories[1]
[<Category: Django>]
bulk_create()
不会在创建的对象上设置主键!