Django:获取切片后无法更新查询

xpa*_*nta 21 django django-queryset

我想这样做:

UserLog.objects.filter(user=user).filter(action='message').filter(timestamp__lt=now)[0:5].update(read=True)
Run Code Online (Sandbox Code Playgroud)

但我收到这个错误:

Cannot update a query once a slice has been taken.
Run Code Online (Sandbox Code Playgroud)

(使用django 1.2.1)

我究竟做错了什么?

Jon*_*nan 53

文档建议是像下面这样也许有可能-我不知道如果这样做的限制内QuerySet绕过围绕需要检查update()切片后:

inner_q = UserLog.objects.filter(user=user,
                                 action='message',
                                 timestamp__lt=now).values('pk')[0:5]
UserLog.objects.filter(pk__in=inner_q).update(read=True)
Run Code Online (Sandbox Code Playgroud)

如果做不到这一点,您可以使用in字段查找,如下所示:

ids = UserLog.objects.filter(user=user,
                             action='message',
                             timestamp__lt=now).values_list('pk', flat=True)[0:5]
UserLog.objects.filter(pk__in=list(ids)).update(read=True)
Run Code Online (Sandbox Code Playgroud)

  • 人们可能应该将它们放在“with transaction.atomic():”块中。 (3认同)
  • 第一种方法对我有用.没试过第二个. (2认同)

Dan*_*per 21

如错误所述,update()如果您取出切片,则无法调用QuerySet.

原因:

  1. 获取切片相当于LIMITSQL中的语句.
  2. 发布更新会将查询转换为UPDATE语句.

你要做的就是等同于

UPDATE ... WHERE ... LIMIT 5

这是不可能的,至少不是标准的SQL.

  • 非常感谢你.我看到了我的错误.这有什么解决方法吗?(除了循环pks并更新每个?) (5认同)
  • `UPDATE...WHERE...LIMIT 1` 在 MySQL 中是可能的。避免“SELECT ... FOR UPDATE”锁非常有用。 (2认同)

Luc*_*s B 5

从 Django 2.2 开始,您可以使用批量更新:

queryset = UserLog.objects.filter(user=user).filter(action='message').filter(timestamp__lt=now)
bulk = []
for userlog in queryset[0:5]:
    userlog.read = True
    bulk.append(userlog)
UserLog.objects.bulk_update(bulk,['read'])

Run Code Online (Sandbox Code Playgroud)