Reputation: 2643
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
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')
Upvotes: 5
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
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:
added?
as the previous validator did.:taken
) Upvotes: 7
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