从对象列表中获取对象,无需额外的数据库调用-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是一个对象列表。对不起,我只是不明白是什么让你困惑——确切地说是效率低下,确切地说是我在问题中描述的问题。所以我在寻找一种有效的方法