【问题标题】:New data not persisting to Rails array column on Postgres新数据未保存到 Postgres 上的 Rails 数组列
【发布时间】:2013-06-21 10:54:23
【问题描述】:

我有一个用户模型,其中包含文本类型的朋友列。运行此迁移以将数组功能与 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

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

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

这可能是什么?

【问题讨论】:

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


    【解决方案1】:

    我怀疑 ActiveRecord 没有注意到您的 friends 数组已更改,因为当您:

    self.friends.push(target)
    

    这将改变数组的内容,但数组本身仍然是同一个数组。我知道这个问题出现在 Rails3 中的 postgres_ext gem 并给出 this issue:

    字符串属性不被标记为脏,当它更改为<<

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

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

    update_attributes friends: self.friends + [ target ]
    

    在将元素添加到现有数组时,有很多方法可以创建新数组,请使用您喜欢的任何一种。

    【讨论】:

      【解决方案2】:

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

      我找不到更主要的来源 atm 但this post 说:

      在模型上与数组(或其他可变值)交互时需要注意一件重要的事情。 ActiveRecord 当前不跟踪“破坏性”或原地更改。其中包括数组pushing 和poping、advance-ing DateTime 对象。如果您想使用“破坏性”更新,您必须调用 <attribute>_will_change! 让 ActiveRecord 知道您更改了该值。

      【讨论】:

      • <attribute>_will_change! 的使用对我来说很容易,并且在代码中似乎读得很好。它的好处(我认为)是对数组属性的所有更改都发生在内存中,并且不会对数组的每次更改都进行数据库查询。当然,权衡是您需要将属性标记为已更改。
      【解决方案3】:

      如果你想使用 Postgresql 数组类型,你必须遵守它的格式。从Postgresql docs 输入格式为

      '{10000, 10000, 10000, 10000}'
      

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

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

      即用括号代替大括号。您必须自己进行转换。

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

      注意:此响应适用于 Rails 3,而不是 4。我会离开这里以防将来对某人有所帮助。

      【讨论】:

      • AFAIK Rails 4 支持开箱即用的 PostgreSQL 数组。当你需要查询你正在序列化的东西时,serialize 很讨厌。
      • 不使用 pg 数组会妨碍使用强大的多维 GIN
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-11
      • 2021-06-13
      • 2016-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多