Rails迁移:尝试将列的类型从字符串更改为整数

我在rails应用程序中使用rails generate migrations命令创建了一个表。 这是迁移文件:

class CreateListings < ActiveRecord::Migration def change create_table :listings do |t| t.string :name t.string :telephone t.string :latitude t.string :longitude t.timestamps end end end 

然后我想将纬度和经度存储为整数,所以我试图运行:

 rails generate migration changeColumnType 

并且该文件的内容是:

 class ChangeColumnType < ActiveRecord::Migration def up #change latitude columntype from string to integertype change_column :listings, :latitude, :integer change_column :listings, :longitude, :integer #change longitude columntype from string to integer type end def down end end 

我期待列类型改变但是rake被中止并且出现以下错误消息。 我想知道为什么这不通过? 我在我的应用程序中使用postgresql。

 rake db:migrate == ChangeColumnType: migrating =============================================== -- change_column(:listings, :latitude, :integer) rake aborted! An error has occurred, this and all later migrations canceled: PG::Error: ERROR: column "latitude" cannot be cast to type integer : ALTER TABLE "listings" ALTER COLUMN "latitude" TYPE integer Tasks: TOP => db:migrate (See full trace by running task with --trace) 

注意:该表没有DATA。 谢谢

我引用了关于ALTER TABLE的手册:

如果没有从旧类型到新类型的隐式或赋值转换,则必须提供USING子句。

你需要的是:

 ALTER TABLE列表ALTER经度TYPE整数USING经度:: int ;
 ALTER TABLE列表ALTER纬度TYPE整数USING latitude :: int ;

或者在一个命令中更短更快(对于大表):

 ALTER TABLE listings ALTER longitude TYPE integer USING longitude::int ,ALTER latitude TYPE integer USING latitude::int; 

只要所有条目都可以转换为integer可以使用或不使用数据
如果已为列定义了DEFAULT ,则可能必须删除并为新类型重新创建该DEFAULT

这是关于如何使用ActiveRecord执行此操作的博客文章 。
或者在评论中使用@ mu的建议。 他知道他的Ruby。 我在这里只对PostgreSQL很好。

我会在您的迁移文件中包含原始SQL,如下所示,以便更新schema.rb。

 class ChangeColumnType < ActiveRecord::Migration def up execute 'ALTER TABLE listings ALTER COLUMN latitude TYPE integer USING (latitude::integer)' execute 'ALTER TABLE listings ALTER COLUMN longitude TYPE integer USING (longitude::integer)' end def down execute 'ALTER TABLE listings ALTER COLUMN latitude TYPE text USING (latitude::text)' execute 'ALTER TABLE listings ALTER COLUMN longitude TYPE text USING (longitude::text)' end end 

我知道这有点难看,但我更喜欢删除列并再次添加新类型:

  def change remove_column :mytable, :mycolumn add_column :mytable, :mycolumn, :integer, default: 0 end 

以下是解决问题的更多方法。 对于我的情况,我在购买表中有两列我需要从类型字符串转换为浮点数。

 def change change_column :purchases, :mc_gross, 'float USING CAST(mc_gross AS float)' change_column :purchases, :mc_fee, 'float USING CAST(mc_fee AS float)' end 

这对我有用。

  1. 您是否在这些列中有现有数据?
  2. 您不应该使用int作为纬度和经度。 它们应该是浮点数。

纬度和经度是十进制的

 rails g scaffold client name:string email:string 'latitude:decimal{12,3}' 'longitude:decimal{12,3}' class CreateClients < ActiveRecord::Migration[5.0] def change create_table :clients do |t| t.string :name t.string :email t.decimal :latitude, precision: 12, scale: 3 t.decimal :longitude, precision: 12, scale: 3 t.timestamps end end end 
Interesting Posts