【问题标题】:Changing HStore Column to Array将 HStore 列更改为数组
【发布时间】:2015-03-12 00:53:26
【问题描述】:

我的 Rails/Postgres 应用程序中有一个列,我想将其从 Hstore 更改为 Array。 (我将手机存储为哈希,所以我可以使用{default: 123, mobile: 1234},但我认为它没有必要/没用)

所以我做了以下迁移:

class ChangePhonesToArray < ActiveRecord::Migration
  def up
    new_phones = {}
    Place.find_each do |p|
      new_phones[p.id] = p.phones.values.map{ |v| v.gsub(%r!\D!, '') } # get rid of non-number characters while I'm at it
    end

    remove_column :places, :phones

    add_column :places, :phones, :string, array: true, default: []

    new_phones.each do |k, v|
      p = Place.find(k)
      p.update_attributes!(phones: v)
    end
  end
  ...
end

但是,当我这样做时,我得到了这个令人讨厌的 DB 错误,表明 phones 仍然是一个 Hstore 列!

StandardError: An error has occurred, this and all later migrations canceled:

can't cast Array to hstore/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/abstract/quoting.rb:76:in `type_cast'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/postgresql/quoting.rb:111:in `type_cast'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/postgresql_adapter.rb:828:in `block in exec_cache'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/postgresql_adapter.rb:827:in `map'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/postgresql_adapter.rb:827:in `exec_cache'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/postgresql/database_statements.rb:155:in `exec_delete'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/abstract/database_statements.rb:101:in `update'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/abstract/query_cache.rb:14:in `update'

我认为该列没有被正确删除,所以我在第一个之后立即在其中扔了第二个remove_column,这引发了以下错误,表明该列已被删除! (但显然不完全)。

StandardError: An error has occurred, this and all later migrations canceled:

PG::UndefinedColumn: ERROR:  column "phones" of relation "places" does not exist
: ALTER TABLE "places" DROP "phones"/Users/sasha/.rvm/gems/ruby-2.1.2/gems/rack-mini-profiler-0.9.2/lib/patches/sql_patches.rb:160:in `exec'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/rack-mini-profiler-0.9.2/lib/patches/sql_patches.rb:160:in `async_exec'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/postgresql/database_statements.rb:128:in `block in execute'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/abstract_adapter.rb:373:in `block in log'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activesupport-4.1.4/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/abstract_adapter.rb:367:in `log'
/Users/sasha/.rvm/gems/ruby-2.1.2/gems/activerecord-4.1.4/lib/active_record/connection_adapters/postgresql/database_statements.rb:127:in `execute'

知道这里发生了什么,以及如何修复/解决它吗?

【问题讨论】:

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


    【解决方案1】:

    仅仅因为您更改了数据库中的表架构并不意味着您的Place 类知道这些更改。 ActiveRecord::Base 子类只加载一次列信息,以避免不必要地一遍又一遍地访问数据库。只要你这样做:

    Place.find_each
    

    您的Place 将知道列类型是什么。然后更改Place 背后的模式并尝试写入新值,但Place 不知道这些更改。通常的解决方法是致电reset_column_information:

    重置有关列的所有缓存信息,这将导致它们在下一次请求时重新加载。

    此方法最常见的使用模式可能是在迁移中...

    所以你会说:

    #...
    remove_column :places, :phones
    add_column :places, :phones, :string, array: true, default: []
    Place.reset_column_information
    #...
    

    顺便说一句,不能保证hstore 会保持任何顺序,因此您可能需要p.phones.values_at(:default, :mobile) 而不是p.phones.values 以确保数组中的顺序正确。

    【讨论】:

    • 谢谢!我不知道班级本身需要重置其信息。酷学。像魅力一样工作。
    猜你喜欢
    • 2020-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-22
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    • 2018-02-01
    相关资源
    最近更新 更多