更新Rails中的每个数组对象值

时间:2016-06-29 17:23:41

标签: ruby-on-rails ruby loops ruby-on-rails-5

基本上我想更新Rails 5中的Model的每个表列。

str = "abc---def"

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

旧对象就像:

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

]

新应该是:

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

]

但我得到的是bar: "def"。在轨道上有一个干净的方法来实现我想要的吗? update_attributes会出错。

标题名称是否正确?

2 个答案:

答案 0 :(得分:1)

首先,使用Foo.where(id:1).update_all更新单个记录可能有效,但不是惯用的。最好使用Foo.find_by(id: 1).update。为了获取单个记录,我更倾向于使用find_by而不是find,因为它返回nil而不是引发NotFound错误,但这是个人偏好。

其次,您使用update_all(bar: a)的方式会给您带来意想不到的结果。在map块中,返回的值将成为结果数组的一部分。 update_all不会返回已更改的记录。它返回一个整数,显示已更改的记录数。同样,update不会返回记录。它返回true或false`,具体取决于验证是否通过。

将这些概念结合在一起,可以编写以下代码:

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

或者写另一种做同样事情的方式:

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

请注意,在这些示例中,我使用的safe navigation operator是比2.3更新的Ruby版本。它有助于防止nil对象NoMethodError,但并不是必需的。

答案 1 :(得分:1)

首先让我们从一些基础知识开始。

您想要更新多行,并希望为每一行设置不同的值。所以它不能像你一样在单个查询中完成。因此,您需要遍历Foo个对象并分别设置每个对象。

所以我们假设

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])}

最后一行是循环返回的对象并为每个对象设置bar值。