我有一个对象的导入,如果它已经先前已导入,我想要检查数据库,如果有,我将更新它,如果不是,我将创建一个新的。但是这样做的最佳方式是什么。
现在我有这个:
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
但是这会为new_books中的每本书创建一个数据库调用。所以我正在寻找一种方法,它只会调用一次数据库,然后只从该查询集中获取对象。
Ps:没有找到get_or_create类的东西,因为更新和创建函数比这更复杂:)
---编辑---
我想我的解释不够好,因为答案并没有反映出问题所在。所以要更清楚(我希望):
我想根据该对象的id从查询集中挑选出一个对象。我想要完整的对象,所以我可以更新它并保存它的更改值。所以假设我有一个包含3个对象A,B和C的查询集。然后我想要一种方法来询问查询集是否有对象B,如果它已经得到它,没有额外的数据库调用。
答案 0 :(得分:0)
假设new_books
是Book
的另一个查询集,您可以尝试过滤id
old_books = Book.objects.filter(foreign_source="import").filter(id__in=[b.id for b in new_books])
这个old_books
已经创建了书籍。
答案 1 :(得分:0)
您可以使用values_list('id', flat=True)
在单个数据库调用中获取所有ID(比查询集快得多)。然后你可以使用集来找到交叉点。
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和insert... on duplicated key update
中查看its custom manager for Django选项。
如果以上内容完全偏离赛道,请发表评论。
答案 2 :(得分:0)
您必须执行多个查询。你需要两组对象,你不能同时获取它们和同时将它们分开。没有bulk_get_or_create方法。
但是,您提供的示例代码将对每个对象执行查询 ,这实际上效率不高(或者就此而言是djangoic)。相反,使用__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足够聪明,知道如何在该上下文中使用该new_books查询集(它也可以是一个常规的id列表)
<强>更新强>
Queryset对象只是一种对象列表。所以你现在需要做的就是遍历对象:
for book in old_to_update:
#update book
for book in old_to_create:
#create book
此时,当它从QuerySet中提取图书时,不是来自数据库,这比为每一个使用.get()
提高效率要高得多 - 而且你得到相同的结果。您可以使用对象进行每次迭代,就像从直接.get()
调用中获得它一样。
答案 3 :(得分:0)
我找到的最佳解决方案是使用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 )
这样,每次需要从查询集中获取特定书籍时都不会查询数据库。然后你可以这样做:
if old_book:
#update book
old_book.save()
else:
#create new book
在 Django 1.7 中,有一个update_or_create()方法可以更好地解决这个问题:https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.update_or_create