Update Each Array-Object Value in Rails

时间秒杀一切 提交于 2019-12-25 04:01:17

问题


Basically I want to update each table column for a Model in Rails 5.

str = "abc---def"

str.split('---').map do |a|
 Foo.where(product_id:1).update_all(bar: a)
end

Old object would be like:

[
 [0] { product_id: 1,
       ...,
       bar: "xxx",
       ...
     },
 [1] { product_id: 1,
       ...,
       bar: "xxx",
       ...
     }

]

New should be like:

[
 [0] { product_id: 1,
       ...,
       bar: "abc",
       ...
     },
 [1] { product_id: 1,
       ...,
       bar: "def",
       ...
     }

]

But what I got is bar: "def" for each. Is there a clean method in rails to achieve what I want? update_attributes gives an error.

Is the title name correct?


回答1:


First of all let's get started from some basics.

You want to update multiple rows and want to set different value for each row. So it cannot be done in single query like you are doing. So you need to loop through the Foo objects and set each one separately.

So let's assume

str = "abc---def---ghi---jkl"
tokens = str.split('---') 
foos_to_update = Foo.where(product_id: 1) #Let's assume it will return 4 or lesser records. (otherwise you need to tell what do you wanna do if it returns more then `tokens`)
foos_to_update.each_with_index {|foo,i| foo.update(bar: tokens[i])}

The last line is looping through returned objects and setting the bar value for each object.




回答2:


First of all, using Foo.where(id:1).update_all to update a single record may work, but is non-idiomatic. It's better to use Foo.find_by(id: 1).update. For getting single records, I prefer to use find_by instead of find because it returns nil instead of raising NotFound errors, but that's a personal preference.

Second, the way you're using update_all(bar: a) is giving you unexpected results. In a map block, the returned value becomes part of the resulting array. update_all doesn't return the record which were changed. It returns an integer showing the count of records which were changed. Similarly, update doesn't return the record. It returns true or false` depending on if the validations passed.

Tying together these concepts, the following code can be written:

str = "abc---def"    
str.split('---').map do |a|
 foo = Foo.find_by(id:1)
 foo&.update(bar: a)
 foo
end

# note that you could instead write `foo.update(bar: a)` if you
# don't want to use the safe navigation operator

Or another way to write it which does the same thing:

str = "abc---def"
str.split('---').map do |a|
 Foo.find_by(id:1)&.tap { |foo| foo.update(bar: a) }
end

Note that in these examples I'm using the safe navigation operator which is in Ruby versions newer than 2.3. It helps prevent NoMethodError on nil objects, but isn't really necessary.



来源:https://stackoverflow.com/questions/38106321/update-each-array-object-value-in-rails

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!