从没有额外数据库调用的对象列表中获取对象 - Django

时间:2013-12-12 12:35:33

标签: 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:
        #create book

但是这会为new_books中的每本书创建一个数据库调用。所以我正在寻找一种方法,它只会调用一次数据库,然后只从该查询集中获取对象。

Ps:没有找到get_or_create类的东西,因为更新和创建函数比这更复杂:)

---编辑---

我想我的解释不够好,因为答案并没有反映出问题所在。所以要更清楚(我希望):

我想根据该对象的id从查询集中挑选出一个对象。我想要完整的对象,所以我可以更新它并保存它的更改值。所以假设我有一个包含3个对象A,B和C的查询集。然后我想要一种方法来询问查询集是否有对象B,如果它已经得到它,没有额外的数据库调用。

4 个答案:

答案 0 :(得分:0)

假设new_booksBook的另一个查询集,您可以尝试过滤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

相关问题