11

我有以下 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列,或者只是在它上面旋转,或者将线程暂停几毫秒并再次检查,但这些感觉非常糟糕和肮脏。我也在考虑创建一个存储过程,但这并不是真正独立于数据库的。

4

1 回答 1

20

锁定查询是在 1.4 中添加的。

with transaction.commit_manually():
  ActivePlayListEntry.objects.select_for_update().filter(...)
  aple = ActivePlayListEntry.objects.get(...)
  aple.state = ...
  transaction.commit()

但是您应该考虑重构,以便使用带有 a 的单独表ForeignKey来指示“活动”歌曲。

于 2012-08-10T20:05:59.103 回答