什么是在Django中锁定对象的最简单方法?
什么是在Django中锁定对象的最简单方法?
当其他用户在 update_object 页面处于活动状态时,我希望在用户尝试删除对象时触发错误。我感觉需要一种类似于互斥锁的锁定机制。您有什么建议吗?
admin 更改状态以发布 2023年5月23日
所以,有几种方法可以做你所要求的事情。但其中很多方法都不是独立于实现的:你可以使用锁或递归锁,但它们只能在百分百线程化的服务器上工作,并且在使用fork/pre-fork实现时可能根本不起作用。
这几乎意味着锁定实现将由您自己决定。 两个想法:
- 在您的文件系统上使用
.lock
文件 - 在您的模型类中使用
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
无论如何,也许有一些方法可以混合和匹配这些建议来适合您的口味?
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
修饰符。