ActiveRecord:确保只有一个记录具有特定的属性值?

ActiveRecord:确保只有一个记录具有特定的属性值?

问题描述:

我有一个种族在许多种族在各州的表。但我需要确保只有一个种族被标记为current = true。以下是我在Race模型验证中使用的内容。ActiveRecord:确保只有一个记录具有特定的属性值?

# current: boolean 
validate :only_one_current 

private 
def only_one_current 
    if self.current && (Race.current_race.id != self.id) 
    errors.add(:base, "Races can have only one current race") 
    end 
end 

这似乎工作大部分时间,但偶尔它不会,我不知道为什么。当它不起作用时,它不允许在删除当前不同的记录之后立即保存新记录,其中current = t。我认为这与AR的坚持有关。

必须有更好的方法来做到这一点?

你的问题实际上超出ActiveRecord的。无论您如何实现before_save方法,竞争条件总是可能发生(无双关语),并且两条记录在数据库中具有current = true。有关更多信息,请参阅Concurrancy and Integrity section for validates_uniqueness_of

核心问题是检查记录是否具有current = true的操作以及将记录设置为current = true的操作不是原子操作。这个问题经常出现在并发系统中。

要解决此问题,您需要在数据库中使用unique key index。我建议您将当前标志更改为优先领域。优先级是一个具有唯一键索引的整数。数据库将保证没有两个记录同时具有相同的优先级值。 “当前”比赛将始终是具有最高优先价值的比赛。

比赛条件实际上仍然存在 - 你现在只是有一种方法来检测它。当您将竞争设置为最新(通过查询表格获取最大优先级值)时,如果另一条记录当前拥有与您要保存的优先级相同的优先级值,则会生成异常。只需捕获重复键例外,然后重试。

你需要调用这个作为before_save,而不是作为一个验证:

before_save :only_one_current 
+0

我想我试过了(一段时间)。仍然有问题,不能准确记得。代码是否正确或者是否有更好的? – Karl 2011-01-20 17:16:18