使用单表继承验证多个子类之间的唯一性 [英] validate uniqueness amongst multiple subclasses with Single Table Inheritance

查看:45
本文介绍了使用单表继承验证多个子类之间的唯一性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个具有许多 CardSet 的 Card 模型和一个通过 Membership 模型具有许多 Cards 的 CardSet 模型:

I have a Card model that has many CardSets and a CardSet model that has many Cards through a Membership model:

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

我也有一些上面使用单表继承的子类:

I also have some sub-classes of the above using Single Table Inheritance:

class FooCard < Card
end

class BarCard < Card
end

class Expansion < CardSet
end

class GameSet < CardSet
  validates_size_of :cards, :is => 10
end

以上所有内容都按我的意图工作.我想弄清楚的是如何验证卡只能属于一个扩展.我希望以下内容无效:

All of the above is working as I intend. What I'm trying to figure out is how to validate that a Card can only belong to a single Expansion. I want the following to be invalid:

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**

但是,游戏集应该允许这种行为:

However, GameSets should allow this behavior:

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 调用,但我不确定把它放在哪里.有什么建议吗?

I'm guessing that a validates_uniqueness_of call is needed somewhere, but I'm not sure where to put it. Any suggestions?

我按照建议修改了扩展类:

I modified the Expansion class as 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.!

我说话有点太快了.上述解决方案运行良好,直到我使用新卡更新扩展.它错误地将后续的 #valid? 检查识别为 false,因为它在数据库中发现了自己.我通过在验证方法中添加对 #new_record? 的检查来解决此问题:

I spoke a little too soon. The above solution was working great until I went to update an Expansion with a new card. It was incorrectly identifying subsequent #valid? checks as false because it was finding itself in the database. I fixed this by adding a check for #new_record? in the validation method:

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

推荐答案

真的不确定这一点,我只是在尝试,因为我无法在这里进行测试... 但也许像下面这样的东西适合你.让我知道是否有:]

I'm really not sure about that, I'm just trying because I'm not able to test it here... but maybe something like the following works for you. Let me know if it does :]

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

这篇关于使用单表继承验证多个子类之间的唯一性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆