Django Transactions,select_for_update,并发修改?

时间:2016-03-21 22:20:26

标签: django transactions django-views

我有一堆视图,我在执行前检查某些条件是否为真。首先执行哪个视图并不重要,但是我担心一个用户使用在另一个视图中检查条件后更改数据的视图。非常感谢任何帮助。

我正在使用设置ATOMIC_REQUESTS = True的2 scoops建议来包装事务中的每个请求。我的网站流量相对较低,所以我不关心性能。也使用postgres。

def feed_dog(self, dog_id):
    dog = get_objects_or_404(Dog, pk=dog_id)
    if not dog.removed and not dog.fed_today:    # line 3
        # do additional checks
        dog.fed_today = True    # line 5
        dog.save()
        # also modify other related objects
        treat = Treats.objects.last()
        treat.dogs_being_fed_with_this_treat.add(dog)
        treat.save()

def remove_dog_from_feed_list(self, dog_id):
    dog = get_objects_or_404(Dog, pk=dog_id)
    if not dog.fed_today           # line 12
    dog.removed = True
    dog.save()
    treat.dogs_being_fed_with_this_treat.remove(dog)
    treat.save()

所以我关心的是有人调用feed_dog视图,然后在第3行被检查之后但在第5行启动之前,另一个用户调用了remove_dog函数。由于当用户2正在调用remove_dog时,dog.fed_today仍为False,因此第12行为True,并且remove_dog视图继续执行并且执行dog.removed = True。同时feed_dog视图继续并设置dog.fed_today = True,将狗添加到treat数组等等。所以我有一个不一致的状态dog.removed = True,dog.fed_today = True。

基本上我需要它,以便Dog模型上的某些属性不能同时为True,如dog.fed_today = True和dog.removed = True。我也有这个相关模型Treat,我不希望dog对象删除= True并且也在treat.dogs_being_fed数组中。

问:这是一个有效的问题吗?由于事务提供隔离,这是否意味着其中一个视图在另一个视图之前或之后全部访问数据(他们无法看到处于半完成状态的数据)?

问:从Read Committed增加Django / Postgres的隔离属性会有帮助吗?

问:对Dog模型本身有限制是否有帮助(比如有一个def清除方法,说不能同时删除Dog = True和fed_today = True?)。我主要是在查看视图中的内容。这会在交易中反映出来吗?我是否也能够创建一个反映多个模型中属性的约束,比如不能拥有dog.removed = True并让那条狗在Treats.dogs_being_fed数组中?

问:select_for_update的目的是什么,这会有帮助吗?如果事务已经被认为是隔离的,那么select_for_update的目的是什么?

感谢任何帮助!

1 个答案:

答案 0 :(得分:1)

除非您了解数据库隔离级别,否则您实际上无法有效使用事务。如果您正在使用PostgreSQL,请查看their documentation on the subject

回答您的具体问题:

这是一个有效的问题吗?

绝对。默认的READ COMMITTED隔离级别不会保护数据库免受上面代码的影响。

由于事务提供隔离,这是否意味着其中一个视图在另一个视图之前或之后全部访问数据(他们无法看到处于半完成状态的数据)? / em>的

没有。这大约是SERIALIZABLE隔离级别的意思。

READ COMMITTED增加Django / Postgres的隔离属性会有帮助吗?

是的,但是您通常不需要更严格的隔离级别,并且由于使用它们会有性能损失,因此重新考虑数据库访问模式通常更有意义。

Dog模型本身有限制(比如使用clean()方法)会有帮助吗?

没有。 Django的验证方法适用于查询数据库后创建的Python对象。它们无助于防止因竞争条件造成的数据损坏。 (但事实上,他们可能会帮助检测。)

如果事务已经被认为是隔离的,那么select_for_update()的目的是什么?

select_for_update()执行SELECT但锁定匹配的行,以便在其他事务中不能同时修改它们。如上所述,这不是事务隔离级别所做的事情。

这会有帮助吗?

是的!这是您问题的最简单的解决方案,因为您正在Dog表中选择(并可以锁定)单行。如果两个函数都使用

dog = Dog.objects.filter(pk=dog_id).select_for_update().get()
然后他们都会尝试锁定有问题的行。如果尝试进行并发修改,则第二个将等到第一个事务结束后再继续。