I created a table in my rails app with rails generate migrations command. Here is that migration file:
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
Then I wanted to store the latitude and longitude as integers so
I tried to run:
rails generate migration changeColumnType
and the contents of that file are:
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
I was expecting the column type to change however the rake was aborted and the following error message appeared. I was wondering why this did not go through? Im using postgresql in my app.
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)
NOTE: The table has no DATA.
Thanks
I quote the manual about ALTER TABLE
:
A USING clause must be provided if there is no implicit or assignment
cast from old to new type.
What you need is:
ALTER TABLE listings ALTER longitude TYPE integer USING longitude::int;
ALTER TABLE listings ALTER latitude TYPE integer USING latitude::int;
Or shorter and faster (for big tables) in one command:
ALTER TABLE listings ALTER longitude TYPE integer USING longitude::int
,ALTER latitude TYPE integer USING latitude::int;
This works with or without data as long as all entries are convertible to integer
.
If you have defined a DEFAULT
for the column, you may have to drop and recreate that for the new type.
Here is blog article on how to do this with ActiveRecord.
Or go with @mu's advice in the comment. He knows his Ruby. I am only good with the PostgreSQL here.
I would include the raw SQL in your migration file like below so that it updates 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
I know this a bit ugly, but I prefer to just remove the column and add again with the new type:
def change
remove_column :mytable, :mycolumn
add_column :mytable, :mycolumn, :integer, default: 0
end
The following is a more rails way
to approach the problem. For my case I had two columns in my purchases table that I needed to convert from type string to float.
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
That did the trick for me.
latitude and longitude is decimal
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