0

我有一个具有许多 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
4

2 回答 2

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
于 2010-06-10T23:11:10.853 回答
1

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

例如

validates_uniqueness_of :your_attribute_id, 范围: :type

于 2019-07-18T10:41:21.237 回答