Вопрос

I'm trying to update the attributes of an Object, but often the Object i try to update doesn't exist anymore.

E.g.: I'm post-processing a CSV file to get the attributes:

array.each do |a|
  player = Player.find_by_grepo_id(a[:grepo_id])
  player.update_attributes(a)
end

That will throw an error when a Player is not found.

What i learned from previous experiences:

ActiveRecord::Base.find always throws an exception if it does not find a record, this is intentional. I should only use find if I'm absolutely expect to have whatever it is I'm looking for. If I'm rending a show action and can't find the article, I should rescue that exception and render a 404 (Not found) instead of redirecting to index (technically).

If I want to find something by it's id attribute without forcing an exception, I should use the dynamic finder find_by_id (In my case find_by_grepo_id) which will return false if it doesn't find a record with that id.

But upon running the task which contains the above code i get

NoMethodError: undefined method `update_attributes' for nil:NilClass

That's because a Player with that specific id doesn't exist anymore. If i wrap the update_attributes call in a .present? method it works.

What am i missing? Shouldn't the find_by_id method NOT throw an error and just skip it ?

Это было полезно?

Решение

If you want to do it in one call instead of two, you can use the update_all method like this:

Player.where(:grepo_id => a[:grepo_id]).update_all(a)

This will result in the following SQL:

UPDATE players SET ... = ..., ... = ... WHERE players.grepo_id = ...

Also works if the grepo_id doesn't exist: nothing will get updated. Note however that this just runs the SQL; any validations or callbacks on your model are ignored.

Другие советы

This is due to you are doing update_attributes even if it does not find the record by grepo_id. find_by_grepo_id returns nil if it does not find any record. So you need to add a condition to get rid of this error.

array.each do |a|
  player = Player.find_by_grepo_id(a[:grepo_id])
  player.update_attributes(a) if player.present?
end

Rails has a try method (check the docs) and you can use it here:

array.each do |a|
  player = Player.find_by_grepo_id(a[:grepo_id])
  player.try do |p|
   p.update_attributes(a)
  end
end

This should work fine and updating attribute or silently failing (without throwing exceptions) when no record is found.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top