我在验证方面遇到了一些困难。我正在使用四个模型 - Animal
Cat
Dog
和Breed
。 Animal
是一个抽象类 - 是的,我意识到Rails没有那些,但这就是我使用它的方式;它永远不会直接初始化,但它内部存在许多常见的逻辑供其所有孩子使用。我也在使用STI,因此Animal
,Cat
和Dog
使用相同的表格。
Breed
同时与Animal
处于has_and_belongs_to_many关系,但我想根据所使用的模型限制可用的品种。所以Breed有一个属性animal_type
,它是一个字符串,对应于子类。对此属性的验证是我遇到的问题。我正在努力确保你只能为一种存在的动物创造一个品种。
最后,出于组织目的,Animal
和Breed
位于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.connection
和Dog.connection
所以它们出现在Animal.descendants
数组中),或者是模块(因为Animal
和Breed
在同一个模块中,我很确定我的引用是正确的。)
我被困住了,而且我不是一个足够先进的用户,知道下一步该去哪里。
答案 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>