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

rai*_*400 22 arrays postgresql ruby-on-rails ruby-on-rails-4 rails-activerecord

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

add_column    :users, :friends, :text, array: true
Run Code Online (Sandbox Code Playgroud)

用户模型具有以下方法:

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
Run Code Online (Sandbox Code Playgroud)

以下规范通过,直到我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
Run Code Online (Sandbox Code Playgroud)

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

使用Rails 4.0.0.rc2和pg 0.15.1

这可能是什么?

mu *_*ort 25

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

self.friends.push(target)
Run Code Online (Sandbox Code Playgroud)

这将改变数组的内容,但数组本身仍将是相同的数组.我知道这个问题与Rails3中的postgres_ext gem一起出现并给出了这个问题:

字符串属性在更改时未标记为脏 <<

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

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

update_attributes friends: self.friends + [ target ]
Run Code Online (Sandbox Code Playgroud)

在向现有数组添加元素时,有很多方法可以创建新数组,使用您喜欢的任何一个.


Jer*_*nes 8

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

我找不到更多的主要来源atm,但这篇文章说:

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