使用单表继承验证多个子类之间的唯一性

时间:2010-06-10 21:00:17

标签: ruby-on-rails single-table-inheritance

我有一个Card模型,它有许多CardSet和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**

但是,GameSet应该允许这种行为:

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

2 个答案:

答案 0 :(得分:1)

真的对此不确定,我只是在努力,因为我无法在这里进行测试...但也许类似下面的内容适合你。如果有,请告诉我:]

class Expansion < Set 
   validate :validates_uniqueness_of_cards

   def validates_uniqueness_of_cards
      membership = Membership.find(:first, :include => :set,
         :conditions => ["card_id IN (?) AND set.type = ?",
            self.cards.map(&:id), "Expansion"])
      errors.add_to_base("Error message") unless membership.nil?
   end
end

答案 1 :(得分:0)

在这里参加聚会很晚,但是假设您已经设置了STI,那么您现在就可以验证范围为sti类型的属性的唯一性,

例如

validates_uniqueness_of:您的属性ID,范围::类型