我是否可以验证ActiveRecord中的单个属性?
就像这样:
ac_object.valid?(attribute_name)
我是否可以验证ActiveRecord中的单个属性?
就像这样:
ac_object.valid?(attribute_name)
valid?
,因为它会做更多不必要的事情。ActiveModel::Validations
的validators_on
方法。Post
的title
:class Post < ActiveRecord::Base
validates :body, caps_off: true
validates :body, no_swearing: true
validates :body, spell_check_ok: true
validates presence_of: :title
validates length_of: :title, minimum: 30
end
当 no_swearing
和 spell_check_ok
是非常昂贵的复杂方法时,我们可以采取以下措施:
def validate_title(a_title)
Post.validators_on(:title).each do |validator|
validator.validate_each(self, :title, a_title)
end
end
这将仅验证标题属性而不调用任何其他验证。
p = Post.new
p.validate_title("")
p.errors.messages
#=> {:title => ["title can not be empty"]
我不完全确定我们是否应该安全地使用validators_on
,因此我建议在validates_title
中以合理的方式处理异常。
def valid_attribute?(attribute_name)
self.valid?
self.errors[attribute_name].blank?
end
或者将它添加到 ActiveRecord::Base
中
我最终在 @xlembouras 的回答基础上构建,并将此方法添加到我的 ApplicationRecord
中:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def valid_attributes?(*attributes)
attributes.each do |attribute|
self.class.validators_on(attribute).each do |validator|
validator.validate_each(self, attribute, send(attribute))
end
end
errors.none?
end
end
然后我可以在控制器中做这样的事情:
if @post.valid_attributes?(:title, :date)
render :post_preview
else
render :new
end
errors.none?
可能涉及其他错误(例如,如果此函数之前针对不同的属性运行过)。可能的改进:如果您只关心布尔标志而不是实际错误,则可以考虑在循环中返回:return false unless self.errors[attribute].blank?
。我认为self[attribute]
比send(attribute)
更好,但这只是个人喜好。另外,您可以将其提取到concern中,而不是在父类中拥有它(再次说明偏好)。 - thisismydesignnone?
之前对errors
进行切片:errors.slice(*attributes).none?
。不过,这不是ActiveModel::Validations通常处理事情的方式。值得一提的是,使用[]
访问属性与使用send
不同。后者将调用访问器方法,这正是您99/100所需要的。此外,短路意味着并非所有错误都可供视图呈现。这可能是不可取的。 - coreyward在@coreyward的回答基础上,我还添加了一个validate_attributes!
方法:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def valid_attributes?(*attributes)
attributes.each do |attribute|
self.class.validators_on(attribute).each do |validator|
validator.validate_each(self, attribute, send(attribute))
end
end
errors.none?
end
def validate_attributes!(*attributes)
valid_attributes?(*attributes) || raise(ActiveModel::ValidationError.new(self))
end
end
由于validator.validate_each
是一个私有方法,所以我使用其他解决方案时遇到了问题。
这是在Rails 6下工作的一段代码:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def valid_attributes?(*attributes)
attributes.each do |attribute|
self.class.validators_on(attribute).each do |validator|
validator.validate(self)
end
end
errors.none?
end
end
validator.validate(self)
而不是...validate_each
的一个好处是它会考虑到allow_nil
和allow_blank
选项。https://github.com/rails/rails/blob/ae02cd6539d4768718ca7c05201f4514b9f068e9/activemodel/lib/active_model/validator.rb#L150 - Thimo Jansenvalidate
的一个副作用是它可能会验证超出你范围的字段。例如:validates :email, :first_name, presence: true
并调用 valid_attributes?(:email)
,这将同时验证 first_name
。 - Thimo Jansen
self.class.validators_on
并将属性名称作为参数,使其更具可重用性。 - thisismydesign