在我们的数据库中,我们目前有一个字符串country
列的表。这将存储单个国家代码(例如US
)。country
列有索引。现在我们需要在列中存储多个国家代码,因此我们希望将其转换为PostgreSQL String数组。我当前的迁移代码是
def change
reversible do |direction|
change_table :product do |table|
direction.up do
table.remove_index(:country)
table.rename :country, :countries
table.change :countries, :string, array: true
table.index :countries
end
direction.down do
table.remove_index(:countries)
table.change :countries, :string, array: false
table.rename :countries, :country
table.index :country
end
end
end
end
然而,当我运行迁移时,我得到错误
PG::DatatypeMismatch: ERROR: column "countries" cannot be cast automatically to type character varying[]
HINT: You might need to specify "USING countries::character varying[]"
,我不确定如何指定我希望如何执行转换。
我想知道如何更改我的迁移,以便
countries
列是一个数组countries
列被索引country
列的现有字符串值保存到数组
或者换句话说,让
country: 'US'
是
countries: ['US']
这不是进行这种迁移的安全方法。你应该:
- 创建一个迁移,在表上创建一个新列:
class AddCountriesToProduct < ActiveRecord::Migration
def change
add_column :countries, :product, :string, array: true, default: []
end
end
- 更新您的模型以并发地写入两个列,例如在
after_save
回调中,以便对country
的更改传播到countries
。此时,这两列将不会完全同步。 - 保存每个产品记录(例如,
Product.find_each(&:save!)
),以便触发回调,将值从country
推到countries
。此时,两个列将完全同步。 - 更新你的应用程序,使它不再使用
country
属性,只使用countries
属性。在此之后,两列将不再同步,但countries
将有正确的值。 - 创建迁移以从表中删除旧列:
class RemoveCountryFromProduct < ActiveRecord::Migration
def change
remove_column :country, :product
end
end
完成这些步骤后,您可以开始使用countries
列来存储多个字符串。
注意事项:
- 如果你不介意锁表并且可以处理停机,那么可以在一次迁移中完成所有事情,但是对于安全的在线迁移,你不想这样做。
- 你的表被命名为
product
而不是products
,这打破了Rails的惯例。在这个例子中我尊重你的表名,但是你应该考虑修复这个问题。