我有以下django方法:
def setCurrentSong(request, player):
try:
newCurrentSong = ActivePlaylistEntry.objects.get(
song__player_lib_song_id=request.POST['lib_id'],
song__player=player,
state=u'QE')
except ObjectDoesNotExist:
toReturn = HttpResponseNotFound()
toReturn[MISSING_RESOURCE_HEADER] = 'song'
return toReturn
try:
currentSong = ActivePlaylistEntry.objects.get(song__player=player, state=u'PL')
currentSong.state=u'FN'
currentSong.save()
except ObjectDoesNotExist:
pass
except MultipleObjectsReturned:
#This is bad. It means that
#this function isn't getting executed atomically like we hoped it would be
#I think we may actually need a mutex to protect this critial section :(
ActivePlaylistEntry.objects.filter(song__player=player, state=u'PL').update(state=u'FN')
newCurrentSong.state = u'PL'
newCurrentSong.save()
PlaylistEntryTimePlayed(playlist_entry=newCurrentSong).save()
return HttpResponse("Song changed")
基本上,我希望它是这样的,对于给定的player
,在任何给定时间只有一个ActivePlaylistEntry具有'PL'(播放)状态。但是,我有实际经历过的情况,由于连续两次快速调用此方法,我为同一个播放器获得两首歌曲,状态为'PL'。这很糟糕,因为我有其他应用程序逻辑,它依赖于玩家在任何给定时间只有一首播放歌曲的事实(加上语义上,在同一个播放器上同时播放两首不同的歌曲没有意义) 。有没有办法让我以原子方式进行更新?仅使用on_commit_success
装饰器作为事务运行该方法似乎不起作用。是否有办法锁定属于特定播放器的所有歌曲的表格?我正在考虑在我的模型(布尔字段)中添加一个lock
列,并且只是旋转它或暂停线程几毫秒并再次检查,但这些感觉超级hackish和脏。我也在考虑创建一个存储过程,但这并不是真正独立于数据库的。
答案 0 :(得分:11)
with transaction.commit_manually():
ActivePlayListEntry.objects.select_for_update().filter(...)
aple = ActivePlayListEntry.objects.get(...)
aple.state = ...
transaction.commit()
但你应该考虑重构,以便用ForeignKey
的单独表格来表示“活跃”的歌曲。