所以我有两个模型:
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()
在 Unit 上使用,有效地将其用作锁。
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 表中的一行,因此仍然可以同时添加其他 Units 的其他会话。
另一种相关方法可能是将“sessionInsertInProgress”字段添加到 Unit。假设这将自动更新,它将阻止其他并发进程继续在单元上插入会话,同时允许为其他单元预订会话不受阻碍。