Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails validation, knowing which field is invalid?

i had a model which works fine.

class Product < ActiveRecord::Base
  validates_format_of :field1, with: /\A[0-9\+\-\/\s]+\Z/, allow_nil: true
  validates_format_of :field2, with: /\A[0-9\+\-\/\s]+\Z/, allow_nil: true
end

some of my entries in the database are invalid. these are older entries. i want to run a seed which find these invalid entries

Product.all.each do |product|
  next if product.valid?
  # 
end

i want to clear the attributes which are invalid. let me say, Product 1 has the value test in field1 which is invalid. now i want to clear this field, and only this.

how do i find the fields which are invalid? something like product.field1.valid?

like image 978
rob Avatar asked Aug 04 '15 10:08

rob


2 Answers

The Rails api allows you to get the error message by the key with the ActiveModel::Errors#get method:

Product.all.each do |product|
  product.valid? # => run validation callbacks

  product.errors.messages          # => {:field1=>["cannot be nil"]}
  product.errors.get(:field1)      # => ["cannot be nil"]
  product.errors.get(:field2)      # => nil

  # `include?` and `has_key?` works too(it's aliases)
  product.errors.messages          # => {:field1=>["cannot be nil"]}
  product.errors.include?(:field1) # => true
  product.errors.include?(:field2) # => false

  # 
end
like image 118
Philidor Avatar answered Nov 02 '22 17:11

Philidor


Doing it is easy, but there are a couple of things to keep in mind about performance:

  1. you don't want to just load all your products in memory, and iterate over them,
  2. if possible, you should not try to update them one by one.

This might be an acceptable solution:

invalid_ids = []

Product.find_each(batch_size: 200) do |product|
  if !product.valid?
    if product.errors.include?(:field_1)
      invalid_ids << product.id
    end
  end
end

Product.where(id: invalid_ids).update_all(field_1: nil)
like image 4
tompave Avatar answered Nov 02 '22 16:11

tompave