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

wko9yo5t  于 2023-01-10  发布在  Go
关注(0)|答案(5)|浏览(133)

当一个用户试图删除一个对象而其他用户在update_object视图中处于活动状态时,我想引发一个错误。我觉得需要某种类似互斥锁的锁定机制。你有什么建议吗?

ctrmrzij

ctrmrzij1#

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装饰器。

vojdkbi0

vojdkbi02#

因此,有很多方法可以实现你所要求的,但其中很多方法都不是独立于实现的:你可以使用锁或者rlock,但是它们实际上只能在100%线程化的服务器上工作,并且可能在fork/pre-fork实现中根本不起作用。
这或多或少意味着锁的实现将取决于您。
1.文件系统上的.lock文件
1.模型类中的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()

这确实是一个笨拙的实现,您必须进行清理。您可能对创建了不同的关键区域感到不舒服,但是如果您使用数据库作为实现而不使实现复杂得多,我看不出您会做得更好。(一种选择是让锁完全独立于对象,然后在调用保存()方法后更新它们,但我不想编写这样的代码。)如果你真的想使用一个基于文件的锁定系统,那么它也能解决这个问题。如果你有数据库命中妄想症,那么它可能适合你。比如:

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

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

0x6upsns

0x6upsns3#

因为你的范围被限制在删除,而不是更新,一个选择是重新考虑“删除”作为“取消发布”操作的想法。

class MyManager(models.Manager):
    def get_query_set(self):
        super(MyManager, self).get_query_set().filter(published=True)

class MyModel(models.Model):
    objects = MyManager()
    published = models.BooleanField(default=True)
    ... your fields ...

    def my_delete(self):
        self.published = False
        super(MyModel, self).save()

    def save(self):
        self.published = True
        super(MyModel, self).save()

这样,无论何时提交编辑,所有用户都可以看到它......但其他用户仍然可以自由删除项目。这种技术的一个优点是,您不必使用任何额外的逻辑来锁定项目并向用户呈现不同的UI。缺点是,在数据库表中使用了额外的空间,并且很少会出现已删除项目“神奇地”重新出现的情况。
(This这可能只是一个起点,如果你选择了这条路,你可能会想根据你的用例对这个想法做一个变化。)

soat7uwm

soat7uwm4#

我建议使用一个简单的读写锁,因为您不希望阻止用户并发访问对象(仅阻止编辑)。
一般的方法是创建一个函数来维护活动的读取者的计数。当你需要写入这个对象时,你可以创建另一个函数来阻止新的读取者获得访问(想想维护页面),并且可能重定向现有的读取者。一旦没有更多的读取者留下,你就可以完成你的写入,然后解锁对象。

unguejic

unguejic5#

首先,您需要知道有两种锁定机制:乐观锁和悲观锁。here解释了它们的区别。据我所知,Django没有实现悲观锁的包。对于乐观锁,最完整的库是django-concurrency

相关问题