在替换整个数组时,向postgres数组字段添加元素失败

And*_*eis 1 ruby postgresql cancan ruby-on-rails-4

我有一个具有一系列角色的用户模型.

从我的schema.db:

create_table "users", force: true do |t|
  t.string   "roles",         array: true
Run Code Online (Sandbox Code Playgroud)

我的模型看起来像这样:

class User < ActiveRecord::Base
  ROLES = %w(superadmin sysadmin secretary)

  validate :allowed_roles
  after_initialize :initialize_roles, if: :new_record?

  private

  def allowed_roles
    roles.each do |role|
      errors.add(:roles, :invalid) unless ROLES.include?(role)
    end
  end

  def initialize_roles
    write_attribute(:roles, []) if read_attribute(:roles).blank?
  end
Run Code Online (Sandbox Code Playgroud)

问题是当我尝试从控制台添加另一个角色时,例如user.roles <<"new_role",然后是user.save!说是真的,并要求user.roles给我我想要的输出.但是当我问User.find(user_id).roles然后我得到之前没有"new_role"的状态.

对于前者

user.roles
  => ["superadmin"]
user.roles << "secretary"
  => ["superadmin", "secretary"]
user.save!
  => true
user.roles
  => ["superadmin", "secretary"]
User.find(<user_id>).roles
  => ["superadmin"]
Run Code Online (Sandbox Code Playgroud)

更换整个阵列时,它可以按我的意愿工作:

user.roles
  => ["superadmin"]
user.roles = ["superadmin", "secretary"]
user.save!
  => true
user.roles
  => ["superadmin", "secretary"]
User.find(<user_id>).roles
  => ["superadmin", "secretary"]
Run Code Online (Sandbox Code Playgroud)

我正在使用rails 4和postgresql,角色是cancancan gem.

更改其他字段,如user.name,以便预期使用.我在谷歌上做了很多挖掘,但没有帮助.

Fre*_*ung 5

活动记录跟踪哪些列已更改,并仅将这些列保存到数据库.此更改跟踪通过挂钩到setter方法来工作 - 未检测到对位置变异.例如

user.roles << "superuser"
Run Code Online (Sandbox Code Playgroud)

不会被检测为更改.

有两种方法可以解决这个问题.一个是永远不会改变任何Active Record对象属性.在你的情况下,这将意味着轻微的笨拙

user.roles += ["superuser"]
Run Code Online (Sandbox Code Playgroud)

如果你不能/不会这样做,那么你必须告诉Active Record你做了什么,例如

user.roles.gsub!(...)
user.roles_will_change!
Run Code Online (Sandbox Code Playgroud)

让Active Record知道roles属性已更改并需要更新.

如果Active Record更好地处理这个问题会更好 - 当数组列中的更改跟踪不受支持时(当时mysql占据了大部分注意力)

另一种方法是将这些列标记为始终需要保存(非常类似于序列化属性所发生的那样),但是您需要为此修补补丁activerecord.