【问题标题】:Django Cannot update a query once a slice has been taken获取切片后,Django 无法更新查询
【发布时间】:2013-08-27 14:15:34
【问题描述】:

我有一个问题...

message_batch = Message.objects.all()[500]

我不想再次调用数据库来检索对象,而且我已经将它们放在内存中,所以重点是什么。

所以我尝试这样更新:

message_batch.update(send_date=datetime.datetime.now(), status="Sent")

但我收到以下错误消息:

获取切片后无法更新查询。

为什么?周围有这个吗?我想更新内存中已有的对象,而不是再次调用来检索它们。

这是我的完整代码,必须绕过这个......

total = Message.objects.filter(status="Unsent", sender=user,   batch=batch).exclude(recipient_number__exact='').count()

for i in xrange(0,total,500):
    message_batch = Message.objects.filter(status="Unsent").exclude(recipient_number__exact='')[i:i+500]
    # do some stuff here
    # once all done update the objects
    message_batch.update(send_date=datetime.datetime.now(), billed=True)

【问题讨论】:

  • 使用 django-memcached 来做。它可能会有所帮助
  • 解决方法是让 django evaluate 切片之前的查询集。像message_batch = Message.objects.all() 然后count = len(message_batch) 然后message_batch = message_batch[:500].update(...)

标签: python django


【解决方案1】:

使用 django 数据库事务以获得最佳性能:

https://docs.djangoproject.com/en/1.5/topics/db/transactions/

例如:

from django.db import transaction

total = Message.objects.filter(status="Unsent", sender=user, batch=batch).exclude(recipient_number__exact='').count()

    for i in xrange(0,total,500):
        message_batch = Message.objects.filter(status="Unsent").exclude(recipient_number__exact='')[i:i+500]
        # do some stuff here
        #once all done update the objects
        message_batch.update(send_date=datetime.datetime.now(), billed=True)

        with transaction.commit_on_success():
            for m in message_batch:
                m.update(send_date=datetime.datetime.now(), billed=True)                    

这将包装例如。如果您不在事务中执行更新,则 5000 行更新到一次数据库调用,而不是调用数据库 5000 次。

【讨论】:

    【解决方案2】:

    您可以通过主键更新对象:

    base_qs = Message.objects.filter(status="Unsent", sender=user, batch=batch).exclude(recipient_number__exact='')
    total = base_qs.count()
    
    for i in xrange(0, total, 500):
        page = list(base_qs[i:i+500])
        page_ids = [o.pk for o in page]
        # Do some stuff here
        base_qs.filter(pk__in=page_ids).update(send_date=datetime.datetime.now(), billed=True)
    

    【讨论】:

    • 这会在你每次使用更新时向数据库发送提交,如果你有很多要更新的条目,性能会受到很大影响。在我的回答中使用交易。
    • 问题是切片后如何使用QuerySet,而不是如何优化SQL查询。您始终可以使用commit_on_success 包装上面的代码。
    猜你喜欢
    • 2011-05-16
    • 2021-03-05
    • 2021-08-17
    • 2012-09-11
    • 1970-01-01
    • 1970-01-01
    • 2016-05-14
    • 2015-10-26
    • 1970-01-01
    相关资源
    最近更新 更多