![](/img/trans.png)
[英]Rails Rspec - model attribute undefined method `>' for nil:NilClass
[英]Rails model attribute is nil in custom validator method
我有一个多态表,我希望能够验证创建时提供的实体 id 确实引用了现有对象。 我编写了一个自定义验证器来执行此操作,当我调用find_or_create_by
时会调用它。 但是,我正在对其执行验证的属性在验证方法中为零。
validate :validate_id, on: :create
...
def validate_id
klass = Object.const_get(entity_type)
return if klass.exists?(entity_id)
errors.add(:entity_id, 'is invalid')
end
entity_type
和entity_id
都是模型成员,并作为参数传递给find_or_create_by
。 我不明白为什么它们在验证方法中为零。
这些值在您的方法中不包含任何内容的原因是因为它们仅适用于该方法,并且该方法不会将它们作为参数。 完成您要做的事情的最佳方法是编写一个适当的自定义验证器,该验证器继承自ActiveModel::EachValidator
并实现validate_each(record, attribute, value)
方法。
尽管您可以摆脱validates_each
块:
validates_each :entity_id do |record, attr, value|
return if Entity.exists?(value)
errors.add(attr, 'is invalid')
end
然而...
为什么不直接使用普通的包含验证器?
因为我现在在 Rails 4.2 上,所以当Entity
记录数足够低以至于不会导致性能问题时,我就是这样做的。
(我会使用更好的错误消息,但为了简单起见使用你的。)
validates :entity_id,
inclusion: { in: proc { Entity.all.pluck(:id) },
message: 'is invalid' }
编辑:我现在看到的问题是您的多态实体意味着上面的示例在没有一些调整的情况下将无法工作,因为Entity
可能是EntityOne
或EntityTwo
两个模型中的任何一个。 您可以轻松地检查其中任何一个是否包含有问题的 ID,如下所示:
validates_each :entity_id do |record, attr, value|
return if EntityOne.exists?(value) || EntityTwo.exists?(value)
errors.add(attr, 'is invalid')
end
或者
validates :entity_id,
inclusion: { in: proc { EntityOne.all.pluck(:id) + EntityTwo.all.pluck(:id) },
message: 'is invalid' }
但这不是一个很好的验证,因为有无效的情况作为有效传递。
您可能不得不求助于从ActiveModel::Validator
继承并实现validate(record)
方法的适当自定义验证器,该方法还接受您可以传入预期实体类型的options
。 它看起来像这样:
# Inside the model:
validates_with(EntityIdValidator, expected_entity: entity_type)
#The custom validator:
class EntityIdValidator < ActiveModel::Validator
def validate(record)
expected_entity = options[:expected_entity]
return if expected_entity.exists?(record.entity_id)
record.errors.add(:entity_id, 'is invalid')
end
end
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.