将 HStore 列更改为数组
Changing HStore Column to Array
我的 Rails/Postgres 应用程序中有一列要从 Hstore 更改为数组。 (我将电话存储为散列,所以我可以 {default: 123, mobile: 1234}
,但决定不是 necessary/useful)
所以我做了以下迁移:
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
然而,当我这样做时,我得到了这个讨厌的数据库错误,表明 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'
知道这里发生了什么,以及如何fix/get绕过它吗?
仅仅因为您更改了数据库中的 table 架构并不意味着您的 Place
class 知道这些更改。 ActiveRecord::Base
subclasses 只加载列信息一次,以避免不必要地一遍又一遍地访问数据库。只要你这样做:
Place.find_each
您的 Place
将知道列类型是什么。然后您更改 Place
背后的架构并尝试写入新值,但 Place
不知道这些更改。通常的解决方法是调用 reset_column_information
:
Resets all the cached information about columns, which will cause them to be reloaded on the next request.
The most common usage pattern for this method is probably in a migration...
所以你会说:
#...
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
以确保您的顺序正确数组。
我的 Rails/Postgres 应用程序中有一列要从 Hstore 更改为数组。 (我将电话存储为散列,所以我可以 {default: 123, mobile: 1234}
,但决定不是 necessary/useful)
所以我做了以下迁移:
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
然而,当我这样做时,我得到了这个讨厌的数据库错误,表明 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'
知道这里发生了什么,以及如何fix/get绕过它吗?
仅仅因为您更改了数据库中的 table 架构并不意味着您的 Place
class 知道这些更改。 ActiveRecord::Base
subclasses 只加载列信息一次,以避免不必要地一遍又一遍地访问数据库。只要你这样做:
Place.find_each
您的 Place
将知道列类型是什么。然后您更改 Place
背后的架构并尝试写入新值,但 Place
不知道这些更改。通常的解决方法是调用 reset_column_information
:
Resets all the cached information about columns, which will cause them to be reloaded on the next request.
The most common usage pattern for this method is probably in a migration...
所以你会说:
#...
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
以确保您的顺序正确数组。