什么是在Django中锁定对象的最简单方法?

6 浏览
0 Comments

什么是在Django中锁定对象的最简单方法?

当其他用户在 update_object 页面处于活动状态时,我希望在用户尝试删除对象时触发错误。我感觉需要一种类似于互斥锁的锁定机制。您有什么建议吗?

admin 更改状态以发布 2023年5月23日
0
0 Comments

所以,有几种方法可以做你所要求的事情。但其中很多方法都不是独立于实现的:你可以使用锁或递归锁,但它们只能在百分百线程化的服务器上工作,并且在使用fork/pre-fork实现时可能根本不起作用。

这几乎意味着锁定实现将由您自己决定。 两个想法:

  1. 在您的文件系统上使用.lock文件
  2. 在您的模型类中使用locked属性

在两种情况下,您都必须手动设置更新的锁对象,并在删除时检查它。 尝试这样的代码:

def safe_update(request,model,id):
    obj = model.objects.get(id)
    if obj.locked:
        raise SimultaneousUpdateError #Define this somewhere
    else:
        obj.lock()
        return update_object(request,model,id)
# In models file
class SomeModel(models.Model):
    locked = models.BooleanField(default = False)
    def lock(self):
        self.locked = True
        super(models.Model,self).save()
    def save(self):
        # overriding save because you want to use generic views
        # probably not the best idea to rework model code to accomodate view shortcuts
        # but I like to give examples.
        self.locked = False
        # THIS CREATES A DIFFERENT CRITICAL REGION!
        super(models.Model,self).save()

这确实是一个笨拙的实现,您必须清理它。您可能不会满意不同的关键区域已经被创建,但如果您将数据库用作实现而不使实现更加复杂,我认为您不会做得更好。 (一个选择是使锁完全分离出来。然后,您可以在调用save()方法后更新它们。但我不想编写那种代码。)如果您真的想使用基于文件的锁定系统,那也可以解决这个问题。 如果你对数据库的压力非常敏感,这可能是适合你的东西。像这样:

class FileLock(object):
    def __get__(self,obj):
        return os.access(obj.__class__+"_"+obj.id+".lock",os.F_OK)
    def __set__(self,obj,value):
        if not isinstance(value,bool):
            raise AttributeError
        if value:
            f = open(obj.__class__+"_"+obj.id+".lock")
            f.close()
        else:
            os.remove(obj.__class__+"_"+obj.id+".lock")
    def __delete__(self,obj):
        raise AttributeError
 class SomeModel(models.Model):
     locked = FileLock()
     def save(self):
         super(models.Model,self).save()
         self.locked = False

无论如何,也许有一些方法可以混合和匹配这些建议来适合您的口味?

0
0 Comments

select_for_update是获取对象锁定的最简单的方法,只要你的数据库支持它。根据 Django 文档,至少 PostgreSQL、Oracle 和 MySQL 支持它。

示例代码:

import time
from django.contrib.auth import get_user_model
from django.db import transaction
User = get_user_model()
@transaction.atomic
def my_example_function():
    my_user = User.objects.all()[0]
    print("Acquiring lock...")
    locked_user = User.objects.select_for_update().get(pk=my_user.pk)
    print(locked_user)
    while True:
        print("sleeping {}".format(time.time()))
        print("holding lock on {}".format(locked_user))
        time.sleep(5)

请注意,您必须在事务内使用select_for_update,因此需要使用@transaction.atomic修饰符。

0