I just migrated my create_supplier migration, then I realized that one of my data type was wrong, so I added another migration which looks like this:-
class ChangePhoneToStringInSuppliers < ActiveRecord::Migration[5.1]
def change
change_column :suppliers, :phone_no_1, :string
change_column :suppliers, :phone_no_2, :string
end
end
After migrating this, I realized that I haven`t pushed my code, so ideally I should rollback till create_suppliers migration and add the changes there itself. When I rollback ChangePhoneToStringInSuppliers, I get following error:-
This migration uses change_column, which is not automatically reversible.
To make the migration reversible you can either:
1. Define #up and #down methods in place of the #change method.
2. Use the #reversible method to define reversible behavior.
I think the method suggested in above error message(and other posts on internet) is a prevention to this problem, rather cure(correct me if I am wrong). How can I rollback this migration now?
you need to update migration file code. remove def change method and instead add both method up and down because change_column migration not supported rollback.
I don't know which column data type you used earlier, so please change it as per your need
class ChangePhoneToStringInSuppliers < ActiveRecord::Migration[5.1]
def up
change_column :suppliers, :phone_no_1, :string
change_column :suppliers, :phone_no_2, :string
end
def down
change_column :suppliers, :phone_no_1, :text
change_column :suppliers, :phone_no_2, :text
end
end
In up method write what you want to do, like change column data type to text,
In down method write if you rollback migration what should it do, like currently your column data type is string and you want back it to string when you rollback than write appropriate code.
Related
I've already created a model in Rails to collect some user information
I created the columns as :string initially but I've since changed the way this data is looked up and entered by using separate populated models.
Now instead of entering into these fields as string - i want these columns to be "references" instead.
Is there an easy way to change from the string to reference without having to create a new model entirely?
*do not need to save the existing data
Is there any data in the strings you would like to save?
Or is it just because it has the same name?
You don't have to create a new model.
You could create a simple migration
remove_column :table, :your_column_name, :string
add_column :table, :your_column_name, :integer, references: :your_parent_model
You can add a temporary string column to save the string column first:
rails g migration add_temporary_string_column_to_model temporary_string_column:string
And run rails console:
SomeModel.all.each do |some_model|
some_mode.temporary_string_column = some_mode.string_column
some_mode.save
end
And now you can change your original string column's type to references which is an int(4) column in MySQL, migration like this:
class ChangeFormatInSomeTable < ActiveRecord::Migration
def change
change_column :some_table, :string_column, :references
end
end
Finally, you can run rails console again to convert the string data to integer like this:
SomeModel.all.each do |some_model|
some_mode.string_column = some_mode.temporary_string_column.to_i
some_mode.save
end
And at last, remove the temporary string column:
rails g migration remove_temporary_string_column_from_model temporary_string_column
Here is another solution, without dropping the column itself (not exactly in my case). I'm not sure though if this is the best solution.
In my case, I have a tickets table that holds purchase_uid in itself. I decided to keep purchases in another table after making the necessary improvements in our backend. Purchases table has uuid as the primary key. Given this background, here is my migration to change my column into a reference.
class AddPurchaseRelationToTickets < ActiveRecord::Migration[5.2]
def up
change_column :tickets, :purchase_uid, :uuid, references: :purchase, foreign_key: true, using: 'purchase_uid::uuid'
end
def down
change_column :tickets, :purchase_uid, :string
end
end
In my case, since string doesn't automatically cast into uuid, purchase_uid were dropped and recreated as well. However, if you decide to keep the column type same, I don't think it will be a problem.
You can create migrations to serve the exact purpose.
rails generate migration AddAddressToUsers address:references
This will create a migration file in db/migrate directory.
Then run: rails db:migrate to run migration and make changes in your database.
Don't forget to create associations in your models (belongs_to, has_many, etc.) depending on your system structure.
Wanted to add a simpler alternative to the accepted answer that preserves data:
class ChangeStringToInt < ActiveRecord::Migration[5.1]
def up
change_column :table_name, :field_name, :integer, null: false, references: :table_referenced, using: 'field_name::integer'
add_index :chapter_actions, :field_name
end
def down
change_column :table_name, :field_name, :string, null: false, using: 'field_name::character varying'
remove_index :table_name, :field_name
end
end
We know, rails ActiveRecord::Migration now have a new method
def change
add_column :accounts, :name, :string
add_index :accounts, :name
change_column :my_table, :some_id, :string
end
But my question is for
change_column :my_table, :some_id, :string
rails do not need to know :some_id's previous type is integer or not.
For example assume :some_id was an integer, after this migration it is converted to string.
when I revert this migration :some_id type should be integer again. Am i right ?? but how can rails understand :some_id previous type was integer.
in previous mehtod self.up and self.down it is written in migration file. so it was not problem. rails can easily find that. but in change method how it is recollected?? Does it check the migration for this table in previous migration files where last data type was definded for :some_id or anything else ??
The change_column is an irreversible migration method. So you cannot reverse this migration using change method. To do this you need to write up and down methods. If you just write this in change method, when you run
rake db:rollback
it will throw this exception
ActiveRecord::IrreversibleMigration
You can read more more on:
http://edgeguides.rubyonrails.org/active_record_migrations.html#changing-columns
I have a migration that removes a column:
def change
remove_column :foos, :bar, :boolean
end
When I try to rake db:rollback that migration, I get the following error:
remove_column is only reversible if given a type.
The ActiveRecord::Migration documentation says that the following is the signature for remove_column:
remove_column(table_name, column_name, type, options)
So my type in this case should be :boolean, and I expect that migration to be reversible. What am I missing?
I can certainly break this out into an up and down migration to avoid this problem, but I'd like to understand why the change syntax isn't working in this case.
Simply adding the 3rd argument (the column's :type) to the remove_column method makes that migration reversible. So the OP's original code actually did work, as in:
remove_column :foos, :bar, :boolean
The rest of this answer was an attempt to discover why this method would not have been working, but the OP ended up getting it to work.
I see somewhat contrary info in the documentation for ActiveRecord::Migration:
Some commands like remove_column cannot be reversed. If you care to define how to move up and down in these cases, you should define the up and down methods as before.
For a list of commands that are reversible, please see ActiveRecord::Migration::CommandRecorder.
And this from ActiveRecord::Migration::CommandRecorder:
ActiveRecord::Migration::CommandRecorder records commands done during a migration and knows how to reverse those commands. The CommandRecorder knows how to invert the following commands:
add_column
add_index
add_timestamps
create_table
create_join_table
remove_timestamps
rename_column
rename_index
rename_table
Anyway, it appears that this documentation is out of date... Digging into the source on github:
The method that's giving you grief is:
def invert_remove_column(args)
raise ActiveRecord::IrreversibleMigration, "remove_column is only reversible if given a type." if args.size <= 2
super
end
I gave this a shot... setup a migration on my Rails 4.1.2 app and the migration worked both ways -- up and down. Here was my migration:
class TestRemoveColumn < ActiveRecord::Migration
def change
remove_column :contacts, :test, :boolean
end
end
I also tried with the :boolean argument missing and got the same error as you're talking about. Are you sure you're on the final version of Rails 4.1.2 -- not one of the release candidates? If you are, I'd suggest putting a binding.pry into the Rails source for the invert_remove_column method to inspect the arguments list and see what's going on. To do so, just run bundle open activerecord and then explore to: lib/active_record/migration/command_recorder.rb:128.
Instead of using change, you use up and down methods to your migration:
def up
remove_column :foos, :bar
end
def down
add_column :foos, :bar, :boolean
end
If you're doing a bulk remove of columns, you can make the migration reversible as follows (since rails 6.1)
def change
change_table :foobar, bulk: true do |t|
t.remove :foo, type: :float
t.remove :bar, type: :int
end
end
I am new to rails, and created a custom migration to change my database structure using Rails Generate. Here is the command I issued: rails g migration users.
Now, in the file it created, I inputed:
class Users < ActiveRecord::Migration
def change
add_column :first_name
add_column :last_name
remove_column :name
end
end
When I run rake db:migrate nothing happens. What do I need to do to fix this?
It's not running at all? It's hard to say based on the info you gave. Perhaps you should try a migration with a more unique name? Something like:
rails g migration ConvertUsersNamesToSingleField
I'm not sure if it's cool to have two migrations with the same name. But with short generic names like Users that might be the problem here. And it usually can't hurt to have a verbose and descriptive migration name, for posterity and clarity.
This questions agree that migration with non unique names don't work: Rails migrations with the same name
But even when ran, this will raise errors. You need to include table names in those column calls, and you need to specify a type when creating fields.
class ConvertUsersNamesToSingleField < ActiveRecord::Migration
def change
add_column :users, :first_name, :string
add_column :users, :last_name, :string
remove_column :users, :name
end
end
I'd like to know the "proper" way to approach adding a relation between two existing classes in Rails 3.
Given existing models: Clown & Rabbit
I'd like to add a reference (belongs_to) from Rabbit to Clown. I start by trying to generate a migration:
rails g migration AddClownToRabbits clown:reference
which gives me a migration that looks like:
class AddClownToRabbits < ActiveRecord::Migration
def self.up
add_column :rabbits, :clown, :reference
end
def self.down
remove_column :rabbits, :clown
end
end
After rake db:migrate on this migration I examine SQLite3's development.db and see a new column: "clown" reference
I guess I was expecting a "clown_id" integer column and a migration that looked like:
class AddClownToRabbits < ActiveRecord::Migration
def self.up
add_column :rabbits, :clown_id
end
def self.down
remove_column :rabbits, :clown_id
end
end
I'm sure :reference is supposed to be equivalent to "t.references :clown" but I can't find the documentation (big surprise). API says add_column: Instantiates a new column for the table. The type parameter is normally one of the migrations native types, which is one of the following: :primary_key, :string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean.
...with no reference to :reference.
If you are using edge rails (4.0) you can use:
rails generate migration AddAddressRefToContacts address:references
As you can see by the docs.
After you set belongs_to in Rabbit, and has_many in Clown, you can do a migration with:
add_column :rabbit, :clown_id, :integer
EDIT: See Paulo's answer below for a more updated answer (Rails 4+)
I'm not sure where you got this idea, but there is no (and never has been) such syntax to do what you want with add_column. To get the behavior you want, you'd have to do t.refences :clown, as you stated. In the background this will call: #base.add_column(#table_name, "#{col}_id", :integer, options).
See here.
EDIT:
I think I can see the source of your confusion. You saw the method call t.reference and assumed it was a datatype because calls such as t.integer and t.string exist, and those are datatypes. That's wrong. Reference isn't a datatype, it's just simply the name of a method, similar to t.rename is.