Django - 使用F()表达式但获得非原子更新

4

我有一个管理员操作,长这样:

def process(modeladmin, request, queryset):
    for reservation in queryset:
        if not reservation.processed:
            reservation.processed = True
            reservation.save()
            item = reservation.item
            item.available = F('available') - reservation.quantity
            item.save()

因此,管理员可以处理“预订”某个“物品”的请求。每当管理员这么做时,“预订”都会被标记为已处理,并且可用的“物品”数量将减少预订中指定的数量。
与所有管理员操作相同,如果管理员同时处理多个“预订”,那么一切都会顺利进行,只要这些“预订”拥有不同的“物品”。但是,如果两个“预订”共享一个“物品”,则可用的“物品”数量仅会减少最后一个被处理的“预订”中指定的数量。
我认为`F()`表达式就是为了解决这个问题:我想对“物品”进行多次更改并使它们增加或减少“物品”的属性,而不会遇到竞争条件。我错过了什么?

我正在查看管理员中处理预订后的item.available值! :-) - Andrea
为什么不使用 Item.objects.filter(reservation=reservation).update(F('available') - reservation.quantity) - ilvar
@ilvar reservation的外键指向item,而不是反过来。 - Andrea
如果您发布您的模型,我们可以给出更准确的答案。否则,update(F('available') - reservation.quantity) 就可以工作。 - FallenAngel
你是否曾经找到了问题的根源或更好地理解了它?根据 Django 文档,它应该可以工作:https://docs.djangoproject.com/en/3.0/ref/models/expressions/#avoiding-race-conditions-using-f。我的简单示例按照我的期望工作,但那是在视图方法中完成的。也许这与从管理操作完成有关,或者可能是一个已经修复的错误。 - aggieNick02
显示剩余3条评论
2个回答

0

这并不是使用 F 对象的正确方式。一旦你将获取与保存步骤分开,实质上就明确地变成了非原子操作。

你应该使用update,所以item部分应该是:

Item.objects.filter(id=reservation.item.id).update(available=F('available')-reservation.quantity)

或类似的东西。


1
文档实际上展示了使用F()表达式来处理竞争条件的方法,就像问题中展示的那样。请参见https://docs.djangoproject.com/en/3.0/ref/models/expressions/#avoiding-race-conditions-using-f - aggieNick02

-2

F()表达式用于查询中,例如当您想在SQL中执行以下操作时:

SELECT * FROM foo WHERE foo_col = bar_col

你需要做的是:

Foo.objects.filter(foo_col=F('bar_col'))

无论如何,您的要求是仅根据最后一次预订减少物品,这意味着您必须在循环预订时进行一些创意。其中一个选项是按项目ID对查询集进行排序,并且每次ID“更改”时,根据该项目的最后一次预订调整可用数量。

这并不完全正确。当您想引用同一模型上的字段时,会使用F()表达式。这可以出于各种原因,包括比较和更新。我发布的代码应该导致查询将item.available减少reservation.quantity,而不是获取item-available的值并将其更改后重新插入。但显然我做错了什么,因为它没有按预期工作。 - Andrea
顺便说一下,“一个项目只应该根据最后一次预订进行减少”这个事实是我面临的问题,而不是要求!它应该根据所有预订进行减少,除非这些预订已经处理过。 - Andrea
1
根据 https://docs.djangoproject.com/en/3.0/ref/models/expressions/#avoiding-race-conditions-using-f,这正是 F() 表达式可以使用的方式之一。 - aggieNick02

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接