【问题标题】:Django - Using F() expression but obtaining non atomic updateDjango - 使用 F() 表达式但获得非原子更新
【发布时间】:2012-05-23 05:15:48
【问题描述】:

我有一个如下所示的管理操作:

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()

因此,管理员可以处理reservationitem。每当他这样做时,reservation 就会被标记为已处理,并且可用的items 的数量会减少reservation 中指定的数量。

就像所有管理员操作一样,管理员可以一次处理多个reservations。如果reservations 具有不同的items,一切都会顺利进行。但如果两个reservations 共享一个item,则可用items 的数量只会减少最后一个reservation 处理中指定的数量。

我认为F() 表达式仅适用于这种情况:我想对item 进行许多更改,并让它们增加或减少item 上的属性,而不会遇到竞争条件。我错过了什么?

【问题讨论】:

  • 你是如何检测到这个的?请记住,您必须调用 get() 才能获取新值...
  • 处理完预订后,我正在查看管理中 item.available 的值! :-)
  • 项目是否在预订查询集中获得select_related
  • 为什么不Item.objects.filter(reservation=reservation).update(F('available') - reservation.quantity)
  • @ilvar reservation 有一个指向 item 的外键,而不是相反

标签: python django django-models race-condition


【解决方案1】:

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 的值并将其插入更改。但显然我做错了什么,因为它没有按预期工作
  • 顺便说一句,“一个项目只能根据最后一次预订减少”是我面临的问题,而不是要求!它应该根据所有预订减少,除非这些预订已经处理
  • 根据docs.djangoproject.com/en/3.0/ref/models/expressions/…,这正是F()表达式可以使用的方式之一。
【解决方案2】:

这并不是你使用 F 对象的真正方式。一旦你将获取和保存的步骤分开,你本质上就是让它明确地成为非原子的。

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

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

或类似的东西。

【讨论】:

猜你喜欢
  • 2013-01-02
  • 2011-12-29
  • 1970-01-01
  • 1970-01-01
  • 2016-02-13
  • 2014-02-21
  • 1970-01-01
  • 2018-09-25
  • 1970-01-01
相关资源
最近更新 更多