所以我有两个模型:
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'字段。假设这将以原子方式更新,它将阻止其他并发进程继续在单元上插入会话,同时允许为其他单元无阻碍地预订会话。