开发者

Rails ActiveRecord: validate single attribute

Is there any way I can validate a single attri开发者_运维问答bute in ActiveRecord?

Something like:

ac_object.valid?(attribute_name)


You can implement your own method in your model. Something like this

def valid_attribute?(attribute_name)
  self.valid?
  self.errors[attribute_name].blank?
end

Or add it to ActiveRecord::Base


Sometimes there are validations that are quite expensive (e.g. validations that need to perform database queries). In that case you need to avoid using valid? because it simply does a lot more than you need.

There is an alternative solution. You can use the validators_on method of ActiveModel::Validations.

validators_on(*attributes) public

List all validators that are being used to validate a specific attribute.

according to which you can manually validate for the attributes you want

e.g. we only want to validate the title of Post:

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

Where no_swearing and spell_check_ok are complex methods that are extremely expensive.

We can do the following:

def validate_title(a_title)
  Post.validators_on(:title).each do |validator|
    validator.validate_each(self, :title, a_title)
  end
end

which will validate only the title attribute without invoking any other validations.

p = Post.new
p.validate_title("")
p.errors.messages
#=> {:title => ["title can not be empty"]

note

I am not completely confident that we are supposed to use validators_on safely so I would consider handling an exception in a sane way in validates_title.


I wound up building on @xlembouras's answer and added this method to my 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

Then I can do stuff like this in a controller:

if @post.valid_attributes?(:title, :date)
  render :post_preview
else
  render :new
end


Building on @coreyward's answer, I also added a validate_attributes! method:

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜