验证包含不同模型的后代是不起作用的

时间:2016-04-09 22:40:42

标签: ruby-on-rails ruby

我在验证方面遇到了一些困难。我正在使用四个模型 - Animal Cat DogBreedAnimal是一个抽象类 - 是的,我意识到Rails没有那些,但这就是我使用它的方式;它永远不会直接初始化,但它内部存在许多常见的逻辑供其所有孩子使用。我也在使用STI,因此AnimalCatDog使用相同的表格。

Breed同时与Animal处于has_and_belongs_to_many关系,但我想根据所使用的模型限制可用的品种。所以Breed有一个属性animal_type,它是一个字符串,对应于子类。对此属性的验证是我遇到的问题。我正在努力确保你只能为一种存在的动物创造一个品种。

最后,出于组织目的,AnimalBreed位于Animals模块中,以便将它们排除在外。

以下是我的代码的外观:

module Animals
    class Animals::Animal < ActiveRecord::Base
    end

    class Animals::Breed < ActiveRecord::Base
        validates :animal_type, inclusion: { in: Animal.descendants.map {|d| d.name}
    end
end

class Cat < Animals::Animal
end

class Dog < Animals::Animal
end

验证者应该生成Animal的所有子模型的名称数组,然后它应该比较字符串animal_type,如果相同的值存在于同一个值中则返回true。阵列。

我正在使用控制台对此进行测试,尽管我手动确保值相等,但它仍然失败。我不确定它是否是由延迟加载引起的(我启动控制台时运行的第一个命令是Cat.connectionDog.connection所以它们出现在Animal.descendants数组中),或者是模块(因为AnimalBreed在同一个模块中,我很确定我的引用是正确的。)

我被困住了,而且我不是一个足够先进的用户,知道下一步该去哪里。

2 个答案:

答案 0 :(得分:1)

看起来类加载会影响您,只要Animals模块一次性加载。在Breed类中的代码运行时,Animal没有后代,因此传递给验证的数组为空。

包含验证允许您指定带有lambda的列表,即

validates :animal_type, inclusion: { in: -> { Animal.descendants.map {|d| d.name}}

在运行验证时调用lambda,因此您不会遇到当前问题。您仍然需要确保加载各种后代类。

答案 1 :(得分:0)

我在你的问题上探讨了一下。最好,我可以说,包含验证器不喜欢被传递Animal.descendants.map {|d| d.name}位。那么你如何进行自定义验证呢?类似的东西:

  module Animals
    class Animal < ActiveRecord::Base
    end

    class Breed < ActiveRecord::Base
      validate :animal_type_is_a_descendant_class

      private

      def animal_type_is_a_descendant_class
        if animal_type.nil?
          errors.add(:animal_type, "can't be blank")
        elsif !defined?(animal_type)
          errors.add(:animal_type, "is not a valid class")
        elsif !Animals::Animal.descendants.include?(animal_type.constantize)
          errors.add(:animal_type, "is not a descendant")
        end
      end

    end
  end

  class Cat < Animals::Animal
  end

  class Dog < Animals::Animal
  end

  class Horse
  end

如果我将其加载到控制台然后执行:

  b = Animals::Breed.new
  b.animal_type = "Cat"
  b.valid?

  b.animal_type = "Dog"
  b.valid?

  b.animal_type = nil
  b.valid?
  b.errors

  b.animal_type = "Horse"
  b.valid?
  b.errors

我明白了:

    irb(main):344:0* b = Animals::Breed.new
    => #<Animals::Breed:0x65639a0>
    irb(main):345:0> b.animal_type = "Cat"
    => "Cat"
    irb(main):346:0> b.valid?
    => true

    irb(main):347:0>
    irb(main):348:0* b.animal_type = "Dog"
    => "Dog"
    irb(main):349:0> b.valid?
    => true

    irb(main):350:0>
    irb(main):351:0* b.animal_type = nil
    => nil
    irb(main):352:0> b.valid?
    => false
    irb(main):353:0> b.errors
    => #<ActiveModel::Errors:0x60b9a30 @base=#<Animals::Breed:0x65639a0 @animal_type=nil, @validation_context=nil, @errors=#<ActiveModel::Errors:0x60b9a30 ...>>, @messages={:animal_type=>["can't be blank"]}>
    irb(main):354:0>

    irb(main):355:0* b.animal_type = "Horse"
    => "Horse"
    irb(main):356:0> b.valid?
    => false
    irb(main):357:0> b.errors
    => #<ActiveModel::Errors:0x60b9a30 @base=#<Animals::Breed:0x65639a0 @animal_type="Horse", @validation_context=nil, @errors=#<ActiveModel::Errors:0x60b9a30 ...>>, @messages={:animal_type=>["is not a descendant"]}>
    irb(main):358:0>