Rails ActiveRecord: validate single attribute

2020-05-29 15:37发布

If there is a way I can validate single attribute in Rails?

Something like:

ac_object.valid?(attribute_name)

I'm gonna use it for AJAX validation of particular model fields. Moving these validations to the Javascript makes code really ugly.

3条回答
放我归山
2楼-- · 2020-05-29 16:14

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.

查看更多
够拽才男人
3楼-- · 2020-05-29 16:21

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
查看更多
聊天终结者
4楼-- · 2020-05-29 16:32

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

查看更多
登录 后发表回答