使用关联表验证关系

时间:2016-01-25 11:16:46

标签: ruby-on-rails activerecord

我有以下型号:

class Property < ActiveRecord::Base
  belongs_to :property_type
  has_many   :variant_properties
  has_many   :variants, through: :variant_properties
end

class PropertyType < ActiveRecord::Base
  has_many :properties
end

class Variant < ActiveRecord::Base
  has_many :variant_properties
  has_many :properties, through: :variant_properties
end

class VariantProperty < ActiveRecord::Base
  belongs_to :property
  belongs_to :variant

  validates_uniqueness_of :property, scope: :property_type
end

我要验证的是,同一Variant的两个属性永远不应属于同一个Property_Type。

有没有办法以 Rails 方式执行此验证?

编辑:

最后使用自定义验证器解决,如@ qaisar-nadeem所示。冗余列也可以,但我认为它不仅仅是一个优化解决方案。

class Property < ActiveRecord::Base

  (...)

  validate :property_type_uniqueness

  private

  def property_type_uniqueness
    unless property_type_unique?
      msg = 'You cannot have multiple property variants with same property type'
      errors.add(:property_id, msg)
    end
  end

  def property_type_unique?
    VariantProperty
      .where(variant: variant)
      .select { |vp| vp.property.property_type == property.property_type }
      .empty?
  end
end

2 个答案:

答案 0 :(得分:1)

验证scope无法访问已连接的表,因此您需要进行自定义验证。

所以有两种选择。

选项1:使用自定义验证器并使用SQL检查是否存在具有相同属性类型的任何属性变体。 自定义验证指南可在http://guides.rubyonrails.org/active_record_validations.html#custom-validators

上找到

选项2:在property_type_id模型中添加冗余列variant_properties,然后添加validates_uniqueness_of :property, scope: :property_type,就像您已经完成的那样

更新

这是自定义验证器

class VariantProperty < ActiveRecord::Base
  belongs_to :property
  belongs_to :variant

  validate :check_property_type_uniqueness

  def check_property_type_uniqueness
   errors.add(:property_id, "You cannot have multiple property variants with same property type") if VariantProperty.joins(:property).where(:property_id=>self.property_id,:variant_id=>self.variant_id,:properties=>{:property_type_id=>self.property.property_type_id}).count > 0
  end
end

答案 1 :(得分:0)

你应该将PropertyType的关系添加到VariantProperty

class VariantProperty < ActiveRecord::Base
  belongs_to :property
  belongs_to :variant
  has_one :property_type, through: :property

  validates :property_type, uniqueness: { scope: :variant_id }
end