多くのCardSetを含むCardモデルと、メンバーシップモデルを介して多くの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
私はExpansionクラスをsugestedとして変更しました:
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?
データベースで自分自身を見つけていたため、後続のチェックを誤ってfalseとして識別していました。#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