Django中的原子条件更新

时间:2013-05-18 22:25:41

标签: django orm concurrency

所以我有两个模型:

class Unit(models.Model):
   name = models.CharField()

class Session(models.Model):
   unit = models.ForeignKey(Unit)
   startDateTime = models.DateTimeField()
   endDateTime = models.DateTimeField() 

用户可以为在用户请求的日期/时间开始和结束的会话预订“单位”。没有单位可以同时使用,我想通过确保每个单位不能预订重叠会话来强制执行。 如果可能的话,我想用一个基础查询来做这个,这可能保证了更新的原子性?

我想出了两种方法,我都不满意:

执行此原始SQL:

insert into "myapp_session" user_id, unit_id, startDateTime, endDateTime 
select 6, 2, '2013-05-18 02:09:02', '2013-05-18 03:09:02' from "myapp_session" 
where not exists 
(select * from "myapp_session" where unit_id=2 AND ("startDateTime" BETWEEN '2013-05-18 02:09:02' AND '2013-05-18 03:09:02' OR "endDateTime" BETWEEN '2013-05-18 02:09:02' AND '2013-05-18 03:09:02'));

如果没有预订的会话与其重叠,则只应插入新会话。是否有可能让ORM做类似的事情?

另一种方法是在单元上使用select_for_update(),有效地将其用作锁。

unitLock = list(Unit.select_for_update().filter(id=2)) # list() forces evaluation, this should block others until we have finished inserting the session?
overlappingSessions = Session.objects.filter(startDateTime__range=[requestedStart, requestedEnd], endDateTime__range=[requestedStart, requestedEnd])
if not overlappingSessions.exists():
    Session.objects.create(unit=2, startDateTime=requestedStart, endDateTime=requestedEnd)
# the lock should be freed as soon as the view function returns

这只会锁定Units表中的一行,因此其他单位的其他会话仍然可以同时添加。

另一种相关方法可能是向Unit添加'sessionInsertInProgress'字段。假设这将以原子方式更新,它将阻止其他并发进程继续在单元上插入会话,同时允许为其他单元无阻碍地预订会话。

0 个答案:

没有答案