我有一个具有许多 CardSet 的 Card 模型和一个通过 Membership 模型具有许多 Card 的 CardSet 模型:
class Card < ActiveRecord::Base
has_many :memberships
has_many :card_sets, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :card
belongs_to :card_set
validates_uniqueness_of :card_id, :scope => :card_set_id
end
class CardSet < ActiveRecord::Base
has_many :memberships
has_many :cards, :through => :memberships
validates_presence_of :cards
end
我还有一些使用单表继承的上述子类:
class FooCard < Card
end
class BarCard < Card
end
和
class Expansion < CardSet
end
class GameSet < CardSet
validates_size_of :cards, :is => 10
end
以上所有内容都按我的意愿工作。我想弄清楚的是如何验证一张卡片只能属于一个扩展。我希望以下内容无效:
some_cards = FooCard.all( :limit => 25 )
first_expansion = Expansion.new
second_expansion = Expansion.new
first_expansion.cards = some_cards
second_expansion.cards = some_cards
first_expansion.save # Valid
second_expansion.save # **Should be invalid**
然而,GameSets 应该允许这种行为:
other_cards = FooCard.all( :limit => 10 )
first_set = GameSet.new
second_set = GameSet.new
first_set.cards = other_cards # Valid
second_set.cards = other_cards # Also valid
我猜想某处需要一个 validates_uniqueness_of 调用,但我不知道该把它放在哪里。有什么建议么?
更新 1
我按照建议修改了扩展类:
class Expansion < CardSet
validate :validates_uniqueness_of_cards
def validates_uniqueness_of_cards
membership = Membership.find(
:first,
:include => :card_set,
:conditions => [
"card_id IN (?) AND card_sets.type = ?",
self.cards.map(&:id), "Expansion"
]
)
errors.add_to_base("a Card can only belong to a single Expansion") unless membership.nil?
end
end
这行得通!谢谢J.!
更新 2
我说话有点太快了。在我用新卡更新扩展之前,上述解决方案效果很好。它错误地将后续#valid?
检查识别为错误,因为它在数据库中发现了自己。#new_record?
我通过在验证方法中添加检查来解决此问题:
class Expansion < CardSet
validate :validates_uniqueness_of_cards
def validates_uniqueness_of_cards
sql_string = "card_id IN (?) AND card_sets.type = ?"
sql_params = [self.cards.map(&:id), "Expansion"]
unless new_record?
sql_string << " AND card_set_id <> ?"
sql_params << self.id
end
membership = Membership.find(
:first,
:include => :card_set,
:conditions => [sql_string, *sql_params]
)
errors.add_to_base("a Card can only belong to a single Expansion") unless membership.nil?
end