从对象列表中获取对象,无需额外的数据库调用-Django
我有一个导入的对象,我想对照数据库检查它是否已经被导入,如果有,我会更新它,如果没有,我会创建一个新的。但最好的方法是什么呢 现在我有这个:从对象列表中获取对象,无需额外的数据库调用-Django,django,Django,我有一个导入的对象,我想对照数据库检查它是否已经被导入,如果有,我会更新它,如果没有,我会创建一个新的。但最好的方法是什么呢 现在我有这个: old_books = Book.objects.filter(foreign_source="import") for book in new_books: try: old_book = old_books.get(id=book.id): #update book except: #cr
old_books = Book.objects.filter(foreign_source="import")
for book in new_books:
try:
old_book = old_books.get(id=book.id):
#update book
except:
#create book
但这会为新书中的每本书创建一个数据库调用。因此,我正在寻找一种方法,它只对数据库进行一次调用,然后从查询集中获取对象
Ps:不寻找get_或_create之类的东西,因为更新和创建函数比这更复杂:)
---编辑---
我想我的解释不够好,因为答案没有反映问题所在。因此,为了更清楚(我希望):
我想根据对象的id从queryset中选择一个对象。我想要完整的对象,这样我就可以更新它并用它的更改值保存它。假设我有一个包含3个对象的查询集,a、B和C。然后我想用一种方法来询问查询集是否有对象B,然后它是否得到了对象B,而不需要额外的数据库调用。假设
新书
是书籍
的另一个查询集,您可以尝试对它的id
进行筛选
old_books = Book.objects.filter(foreign_source="import").filter(id__in=[b.id for b in new_books])
使用此旧书
可以创建已创建的书籍 您可以使用在单个DB调用中获取所有ID(比QuerySet快得多)。然后可以使用集合来查找交点
new_book_ids = new_books.values_list('id', flat=True)
old_book_ids = Book.objects.filter(foreign_source="import") \
.values_list('id', flat=True)
to_update_ids = set(new_book_ids) & set(old_book_ids)
to_create_ids = set(new_book_ids) - to_update_ids
--编辑(包括更新的部分)--
我猜您面临的问题是批量更新,而不是批量获取
如果更新很简单,那么类似的方法可能会起作用:
old_book_ids = Book.objects.filter(foreign_source="import") \
.values_list('id', flat=True)
to_update = []
to_create = []
for book in new_books:
if book.id in old_book_ids:
# list of books to update
# to_update.append(book.id)
else:
# create a book object
# Book(**details)
# Update books
Book.objects.filter(id__in=to_update).update(field='new_value')
Book.objects.bulk_create(to_create)
但是,如果更新很复杂(更新字段依赖于相关字段),那么您可以选中MySQL和中的选项
如果以上内容完全偏离了主题,请留下评论。您必须进行多个查询。您需要两组对象,您不能同时获取它们并像这样任意地将它们拆分。没有批量获取或创建方法 但是,您给出的示例代码将对每个对象执行一个查询,这实际上不是很有效(或者说是djangoic)。相反,请使用
\u in
子句创建智能子查询,然后可以将数据库命中限制为仅两个查询:
old_to_update = Book.objects.filter(foreign_source="import", pk__in=new_books)
old_to_create = Book.objects.filter(foreign_source="import").exclude(pk__in=new_books)
Django足够聪明,知道如何在该上下文中使用新的\u books queryset(它也可以是一个常规的ID列表)
更新
Queryset对象只是一种对象列表。因此,您现在需要做的就是在对象上循环:
for book in old_to_update:
#update book
for book in old_to_create:
#create book
在这一点上,当它从QuerySet中获取图书时,不是从数据库中获取图书,这比为每一本书使用
.get()
要有效得多,并且您会得到相同的结果。每次迭代都要处理一个对象,就像直接调用.get()
一样。我找到的最佳解决方案是使用python next()函数
首先将查询集评估为一个集合,然后使用下一步选择所需的书籍:
old_books = set(Book.objects.filter(foreign_source="import"))
old_book = next((book for book in existing_books if book.id == new_book.id), None )
这样,就不会在每次需要从queryset获取特定书籍时都查询数据库。然后你可以做:
if old_book:
#update book
old_book.save()
else:
#create new book
在Django 1.7中,有一个更新或创建()方法可以更好地解决这个问题:这只会限制旧书中对象的数量,但仍然会给我带来同样的问题,即如果存在旧书,就获取旧书版本,而不必再调用另一个数据库调用Hi yuvi。我不明白为什么我不断地得到不回答问题的答案。你的回答只会给我2个查询集,但我不是在寻找查询集,因为我必须对对象执行操作,而不是查询集。所以我想做的是从我的查询集中选择每个对象,这样我就可以单独对它们执行操作。这就是问题所在->如何从查询集获取pick'get'对象->如何查询查询集以获取其中一个对象,而无需进行数据库调用。获取查询集后,您可以循环对象并像普通列表一样操作它们。此时,您将不再访问数据库(更新对象时除外)。在您自己的帖子中,使用
get
方法将对每个对象执行数据库命中,这是非常低效的。一般来说,除非您正在寻找一个特定的对象,否则没有理由使用get
。但我是。我不是在问如何循环查询集。我问的是如何根据对象的id从查询集中选择对象。这是一个我在其他任何地方都找不到好答案的问题。你可以只做Book.objects.filter(…).get(…)
,但如果你对一堆对象做这件事,效率非常低(这是对每个对象的db查询)。QuerySet是一个对象列表。对不起,我只是不明白是什么让你困惑——确切地说是效率低下,确切地说是我在问题中描述的问题。所以我在寻找一种有效的方法