更新Rails中的每个Array-Object值

基本上我想更新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"每个人都bar: "def" 。 在轨道上有一个干净的方法来实现我想要的吗? update_attributes给出错误。

标题名称是否正确?

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

您想要更新多行,并希望为每一行设置不同的值。 所以它不能像你一样在单个查询中完成。 因此,您需要遍历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值。

首先,使用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`取决于validation是否通过。

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

 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 

请注意,在这些示例中,我使用的是安全导航操作符 ,它是2.3版本的Ruby版本。 它有助于防止nil对象上的NoMethodError ,但实际上并不是必需的。