在 Rails 上保留数据并覆盖 Getter/Setter

Preserving Data on Rails Migration with Override on Getter/Setter

我正在进行迁移,我需要 (a) 更改列的数据类型并 (b) 保留该列中的数据(即,将其转换为新数据类型并将其保留在同一列中)。新列类型要求我覆盖 getters 和 setters,这会导致迁移失败。

有一个用户 table,其中包含用户连接的 IP 地址字段:ip_addr。这目前是一个字符串,我想将其更改为整数。迁移的 up 方法如下所示:

def up
  add_column :users, :curip, :integer
  User.reset_column_information
  User.all.each do |u|
    u.update_attribute :curip, User.ip_str_to_int(u.ip_addr)
  end
  remove_column :users, :ip_addr
  rename_column :users, :curip, :ip_addr
end

User.ip_str_to_int 计算将 IP 地址四边形转换为整数。)

我还有一些方法可以覆盖 ip_addr 的 getter 和 setter 以调用 User.ip_str_to_int 和相应的方法 User.ip_int_to_str。这些方法看起来像这样:

def ip_addr
  val = read_attribute(:ip_addr)
  User.ip_int_to_str(val)
end

def ip_addr=(val)
  write_attribute(:ip_addr, User.ip_str_to_int(val))
end

现在你可能已经猜到问题所在了。当迁移 运行s 时,它会阻塞,因为重写的 getter/setters 期望该列包含一个整数,但在迁移发生时,该列实际上包含一个字符串。我已经分别测试了 migration 和 getter/setter 方法,它们都很好。对于我自己的开发环境,我可以简单地注释掉 getters/setters 到 运行 的迁移,然后将它们放回去。但这是一个问题,不适用于生产。

有什么建议吗?我知道如果我不尝试保留相同的列名就不会遇到这个问题,但更改列名意味着更改其他代码。

环境:sqlite 3; Ruby2.1.4; Rails 3.2.13.

您可以修改 getter 方法来处理数据是字符串的情况...

def ip_addr
  val = read_attribute(:ip_addr)
  return val if val.is_a? String
  User.ip_int_to_str(val)
end