validate uniqueness amongst multiple subclasses with Single Table Inheritance

爱⌒轻易说出口 提交于 2019-12-23 04:24:36

问题


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

and

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

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

UPDATE 1

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

This works! Thanks J.!

Update 2

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

回答1:


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



回答2:


Very late to the party here, but assuming you've set up STI, then you can now validate uniqueness of an attribute scoped to the sti type,

e.g

validates_uniqueness_of :your_attribute_id, scope: :type



来源:https://stackoverflow.com/questions/3018482/validate-uniqueness-amongst-multiple-subclasses-with-single-table-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!