Martin Petrov
Martin Petrov

Reputation: 2643

Validation: how to check for specific error

I know how to check an attribute for errors:

@post.errors[:title].any?

Is it possible to check which validation failed (for example "uniqueness")?

Upvotes: 5

Views: 6919

Answers (4)

Alec Rust
Alec Rust

Reputation: 11353

Regarding checking for uniqueness validation specifically, this didn't work for me:

@post.errors.added?(:title, :taken)

It seems the behaviour has changed so the value must also be passed. This works:

@post.errors.added?(:title, :taken, value: @post.title)

That's the one to use ^ but these also work:

@post.errors.details[:title].map { |e| e[:error] }.include? :taken
@post.errors.added?(:title, 'has already been taken')

Ref #34629, #34652

Upvotes: 5

Eliot Sykes
Eliot Sykes

Reputation: 10083

If you're using Rails 5+ you can use errors.details. For earlier Rails versions, use the backport gem: https://github.com/cowbell/active_model-errors_details

is_duplicate_title = @post.errors.details[:title].any? do |detail|
  detail[:error] == :uniqueness
end

Rails Guide: http://guides.rubyonrails.org/active_record_validations.html#working-with-validation-errors-errors-details

Upvotes: 2

Daniel Rikowski
Daniel Rikowski

Reputation: 72544

Recently I came across a situation where I need the same thing: The user can add/edit multiple records at once from a single form.

Since at validation time not all records have been written to the database I cannot use @David's solution. To make things even more complicated it is possible that the records already existing in the database can become duplicates, which are detected by the uniqueness validator.

TL;DR: You can't check for a specific validator, but you can check for a specific error.

I'm using this:

# The record has a duplicate value in `my_attribute`, detected by custom code.
if my_attribute_is_not_unique?
  # Check if a previous uniqueness validator has already detected this:
  unless @record.errors.added?(:my_attribute, :taken)
    # No previous `:taken` error or at least a different text.
    @record.errors.add(:my_attribute, :taken) 
  end
end

Some remarks:

  • It does work with I18n, but you have to provide the same interpolation parameters to added? as the previous validator did.
  • This doesn't work if the previous validator has written a custom message instead of the default one (:taken)

Upvotes: 7

David
David

Reputation: 2331

By "taken", I assume you mean that the title already exists in the database. I further assume that you have the following line in your Post model:

validates_uniqueness_of :title

Personally, I think that checking to see if the title is already taken by checking the validation errors is going to be fragile. @post.errors[:title] will return something like ["has already been taken"]. But what if you decide to change the error message or if you internationalize your application? I think you'd be better off writing a method to do the test:

class Post < ActiveRecord::Base
  def title_unique?
    Post.where(:title => self.title).count == 0
  end
end

Then you can test if the title is unique with @post.title_unique?. I wouldn't be surprised if there's already a Rubygem that dynamically adds a method like this to ActiveRecord models.

Upvotes: 3

Related Questions