from typing import List
from django.db import transaction
with transaction.atomic():
items_you_want_to_update: List[UserLog] = (
UserLog.objects.select_for_update().filter(
user=user,
action='message',
timestamp__lt=now
)[:5]
# slicing evaluates the queryset and returns a list:
# those row are now locked because of select_for_update()
)
for item in items_you_want_to_update:
item.read = True
# Using bulk_update() instead of .save() on each item to get better performance
UserLog.objects.bulk_update(items_you_want_to_update)
8条答案
按热度按时间8e2ybdfx1#
文档建议类似下面的 * 可能 * 是可能的-我不确定在内部
QuerySet
中进行限制是否会绕过切片后调用update()
的检查:如果做不到这一点,您可以使用
in
字段查找,如下所示:r8uurelv2#
如错误所述,如果取出切片,则不能在QuerySet上调用
update()
。理由是:
1.获取切片相当于SQL中的
LIMIT
语句。1.发出更新会将查询转换为
UPDATE
语句。你要做的事就相当于
UPDATE ... WHERE ... LIMIT 5
这是不可能的,至少对于标准SQL是不可能的。
eivgtgni3#
自Django 2.2起,您可以使用批量更新:
6mw9ycah4#
我在尝试限制查询集返回的记录数时也遇到了同样的错误。
我发现如果我们使用Django的class-based generic views,比如ArchiveIndexView,我们可以使用
paginate_by =
属性来限制记录的数量。例如(在www.example.com中views.py):
5rgfhyps5#
你不能这么做。从Django的文档中可以看出:QuerySet API参考-更新
b4wnujal6#
如果你想切出一个查询集的一些结果,你可以把它复制到另一个变量(浅副本就足够了,它比深副本快,因为它只使用对原始对象的引用)。
如果你的表上有order_by过滤器,这将使Django不会抱怨,因为如果你在主queryset对象上做切片,那是在切片之后发生的
j9per5c47#
我认为这个答案(https://stackoverflow.com/a/4286144/12120968)是可行的,但是如果你担心竞争条件,这里有另一个选择。
有一个
select_for_update()
方法,在事务内部使用时会锁行,这里有[到文档的链接
]1和StackOverlflow中的一个相关帖子:获取切片后无法更新查询
因此,对于您的用例,它可能类似于:
b5lpy0ml8#
您的代码不正确,因为切片发生的位置。它应该发生在调用
update()
* 之后 *,而不是之前。错误:
右: