当某个其他用户在update_object视图中处于活动状态时,我想在用户尝试删除对象时引发错误.我觉得需要某种类似互斥锁的机制.你有什么建议吗?
所以,有一些方法可以做你所要求的.但是其中很多都不是独立于实现的:你可以使用锁或rlocks,但它们实际上只能在100%线程服务器上工作,而在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() target_user_pk = User.objects.all()[0].pk with transaction.atomic(): print "Acquiring lock..." to_lock = User.objects.filter(pk=target_user_pk).select_for_update() # Important! Queryset evaluation required to actually acquire the lock. locked = to_lock[0] print locked while True: print "sleeping {}".format(time.time()) time.sleep(5)