新数据不会持久到Postgres上的Rails数组列

时间:2013-06-24 19:49:25

标签: ruby-on-rails arrays postgresql ruby-on-rails-4 rails-activerecord

我有一个带有text类型的朋友列的用户模型。运行此迁移以使用postgres的数组功能:

add_column    :users, :friends, :text, array: true

用户模型有这种方法:

def add_friend(target)
  #target would be a value like "1234"
  self.friends = [] if self.friends == nil
  update_attributes friends: self.friends.push(target)
end

以下规范通过,直到我在调用user.reload后添加#add_friend

it "adds a friend to the list of friends" do
  user = create(:user, friends: ["123","456"])
  stranger = create(:user, uid: "789")
  user.add_friend(stranger.uid)
  user.reload #turns the spec red
  user.friends.should include("789")
  user.friends.should include("123")
end

这也发生在开发中。模型实例已更新,并且在数组中有新的uid,但是一旦在不同的操作中重新加载或重新加载用户,它将恢复到调用add_friend方法之前的状态。

使用Rails 4.0.0.rc2和pg 0.15.1

这可能是什么?

3 个答案:

答案 0 :(得分:24)

我怀疑ActiveRecord没有注意到你的friends数组已经改变了,因为当你执行时,底层数组引用不会改变:

self.friends.push(target)

这会改变数组的内容,但数组本身仍然是同一个数组。我知道这个问题与Rails3中的postgres_ext gem密切相关并且给出了this issue

  

当字符串属性随<<

更改时,不会标记为脏

我希望Rails4的行为方式相同。

解决方案是创建一个新数组,而不是尝试就地修改数组:

update_attributes friends: self.friends + [ target ]

在向现有数组添加元素的同时,有很多方法可以创建新数组,请使用您喜欢的数组。

答案 1 :(得分:8)

看起来问题可能是您使用push,它会修改数组。

我无法找到更多主要来源atm,但this post说:

  

在模型上与数组(或其他可变值)交互时需要注意的一件重要事项。 ActiveRecord目前不会跟踪“破坏性”或就地更改。这些包括数组pushpopadvance - DateTime对象。如果您想使用“破坏性”更新,则必须致电<attribute>_will_change!让ActiveRecord知道您更改了该值。

答案 2 :(得分:0)

如果要使用Postgresql数组类型,则必须遵守其格式。从Postgresql docs开始,输入格式为

'{10000, 10000, 10000, 10000}'

这不是friends.to_s将返回的内容。在红宝石中:

[1,2,3].to_s => "[1,2,3]"

即括号而不是括号。你必须自己做转换。

但是我更倾向于依赖ActiveRecord序列化(参见serialize)。数据库不需要知道该值实际上是一个数组,这是您的域模型泄漏到您的数据库中。让Rails做它的事情并封装那些信息;它已经知道如何序列化/反序列化该值。

注意:此回复适用于Rails 3,而不是4.我将离开此处,以防将来帮助某人。