Renaming table in rails - ruby-on-rails

I want to rename a table... (any table.)
I tried this line of code:
ActiveRecord::ConnectionAdapters::SchemaStatements.rename_table(old_name, new_name)
Here's the weird thing. I know I got it working the first time, but now I get this error: undefined method `rename_table' for ActiveRecord::ConnectionAdapters::SchemaStatements:Module
Was there something I need to set?

Remember that in Rails >= 3.1 you can use the change method.
class RenameOldTableToNewTable < ActiveRecord::Migration
def change
rename_table :old_table_name, :new_table_name
end
end

You would typically do this sort of thing in a migration:
class RenameFoo < ActiveRecord::Migration
def self.up
rename_table :foo, :bar
end
def self.down
rename_table :bar, :foo
end
end

.rename_table is an instance method, not a class method, so calling Class.method isn't going to work. Instead you'll have to create an instance of the class, and call the method on the instance, like this: Class.new.method.
[EDIT]
In this instance, ActiveRecord::ConnectionAdapters::SchemaStatements isn't even a class (as pointed out by cam), which means that you can't even create an instance of it as per what I said above. And even if you used cam's example of class Foo; include ActiveRecord::ConnectionAdapters::SchemaStatements; def bar; rename_table; end; end;, it still wouldn't work as rename_table raises an exception.
On the other hand, ActiveRecord::ConnectionAdapters::MysqlAdapter is a class, and it is likely this class you'd have to use to rename your table (or SQLite or PostgreSQL, depending on what database you're using). Now, as it happens, ActiveRecord::ConnectionAdapters::MysqlAdapter is already accessible through Model.connection, so you should be completely able to do Model.connection.rename_table, using any model in your application.
[/EDIT]
However, if you wish to permanently rename a table, I would suggest using a migration to do it. It's easy and the preferred way of manipulating your database structure with Rails. Here's how to do it:
# Commandline
rails generate migration rename_my_table
# In db/migrate/[timestamp]_rename_my_table.rb:
class RenameMyTable < ActiveRecord::Migration
def self.up
rename_table :my_table, :my_new_table
end
def self.down
rename_table :my_new_table, :my_table
end
end
Then, you can run your migration with rake db:migrate (which calls the self.up method), and use rake db:rollback (which calls self.down) to undo the migration.

ActiveRecord::Migration.rename_table(:old_table_name, :new_table_name)

Related

Is there any difference in using up/down vs change/reversible methods in Rails migrations?

In a Rails migration, does it make any difference, if I do this:
def up
foo
end
def down
bar
end
or this:
def change
reversible do |direction|
direction.up { foo }
direction.down { bar }
end
end
?
I think that it's better to use the change method if part of the migration includes reversible methods, such as create_table, add_column etc.. Other than that, is there any difference?
As you show it, there is no advantage. The main advantage is that a lot of the time you don't need to write the down method / block at all, eg
class SomeMigration < ActiveRecord::Migration
def change
create_table :articles do |t|
...
end
end
end
The reversible method is mostly used when there is a small part of a migration that activerecord doesn't know how to reverse (eg a raw SQL statement)
The change method works for many cases where the reversible logic can be derived easily and ActiveRecord migrations can easily handle it. However, in some cases you may need to do something specific inside of a migration to insure it will run without errors. There may also be something complex would be an irreversible migration.
See Rails: Is it bad to have an irreversible migration?
In such cases it is best to use the up down method but there is also away of checking if the current direction of the migration which is simply reverting?
Here's an example where you might use this in place of the 2 up/down methods. In this example, there is also a complex data method not show which caused a migration error without the conditional logic. I've ommited as it is not relevant to your question:
def change
add_column :languages, :iso_639_2t, :string
add_column :languages, :iso_639_2b, :string
add_column :languages, :iso_639_3, :string
add_index :languages, :iso_639_2t
unless reverting?
ActiveRecord::Base.connection
.execute("ALTER SEQUENCE languages_id_seq RESTART WITH #{Language.count+1}")
end
end
If complex data manipulation is used inside of a migration via migration_data gem for example, you might want to add a def check_data method and raise an exception if the data_check fails.
If the migration is really irreversible (destroys data), you may want to also use up/down for example:
def up
remove_column :signup, :date
end
def down
raise ActiveRecord::IrreversibleMigration, "Date field dropped in previous migration. Data unrecoverable"
end

How do I create a reversible migration helper in rails?

I find myself having to execute very similar sql statements (with maybe 1 param besides the table name) on several tables on a rails app. As a result, I'm getting lots of similarly looking migrations, like this one:
class DoSomeSQLOnUser < ActiveRecord::Migration
def up
execute('some long sql that alters the user.field1')
execute('some long sql that alters the user.field2')
end
def down
execute('some sql that undoes the changes')
end
end
Then I have the same thing for clients, sales, etc.
I would like to extend ActiveRecord::Migration so that I can do this instead:
class DoSomeSQLOnUser < ActiveRecord::Migration
def change
do_custom_thing_on :users, :field1
do_custom_thing_on :users, :field2
end
end
How can I do that? I think I know how to do it when the operations are separated into up and down, like this:
class DoSomeSQLOnUser < ActiveRecord::Migration
def up
do_custom_thing_on :users, :field1
do_custom_thing_on :users, :field2
end
def down
undo_custom_thing_on :users, :field1
undo_custom_thing_on :users, :field2
end
end
But doing it so that the change is "reversible" escapes me.
It doesn't seem to be a official supported way to do this, so probably you'll need to open the class ActiveRecord::Migration::CommandRecorder and record the new method and its inverted version.
Find the definition of the class at activerecord/lib/active_record/migration/command_recorder.rb.
In Rails 4 there is a reversible helper method, which you can use like this:
def change
do_custom_thing_on :users, :field1
do_custom_thing_on :users, :field2
end
def do_custom_thing_on(table, field)
reversible do |dir|
dir.up { execute "some long sql to alter #{field} on #{table}"}
dir.down { execute "some long sql to undo #{field} on #{table}"}
end
end
Not sure if you need to do something else, but at least you should add a inverse_custom_thing method to ActiveRecord::Migration::CommandRecorder
Main purpose of change method it's adding, renaming columns but not removing. Change method 'knows' how to make reverse when the migration is rolled back. So if you want to do something, which is not removing, just do it in change method, ActiveRecord will reverse it itself, I think.
More information you can get from official documentation

Does change handle migration / rolling back of schema

In the new preferred way, there is only 1 method which seems to add a column. Does that mean you don't need to have a method to remove columns?
# the old way
class AddNameToPerson < ActiveRecord::Migration
def up
add_column :persons, :name, :string
end
def down
remove_column :person, :name
end
end
# the new prefered way
class AddNameToPerson < ActiveRecord::Migration
def change
add_column :persons, :name, :string
end
end
That's the magic of Rails 3.1 and later. Rails knows how to migrate your database and reverse it when the migration is rolled back without the need to write a separate down method.
Rails keep track of migration. So it will have the implementation for reverse the changes added by the migration when you roll back. Earlier we use to have two methods up and down. On Rails 3.1 and later they changed migration to have change method.
In case of add_coulmn reverse method is remove_column and same for other methods. Using tracked detail it will call appropriate action. So you don't need two methods(up and down). You can see the methods supported by change method: here. If you have other then these method you need to use up and down.
If you are confused with using change method then I suggest you to use up and down. Once you thorough with migration you can start using change method.
You are right, You don't need a separate method to remove columns.
You can even try 'rake db:rollback' and then again 'rake db:migrate' if you don't have any data to lose.
It works perfectly. :)

Rails - migrations - change_column_default

I wish to change the default of an integer in my database via migrations
class ChangeColumnDefault < ActiveRecord::Migration
def self.up
change_column_default(:persons, :age, 0)
end
end
I have also tried:
class ChangeColumnDefault < ActiveRecord::Migration
def change
change_column_default(:persons, :age, 0)
end
end
And I just run the migration by:
rake db:migrate VERSION=20120822182554_change_column_default.rb
No errors are displayed in the terminal, and i can see no changes in my schema.rb
Any ideas/hints??
Maybe use just rake db:migrate VERSION=20120822182554
and where is table name? :)
I just tried this out, it worked. It seems like your table would be :people though and not :persons. Also it make the most since to me to use 'def up' and 'def down'.
So lets say you want to set default value to nil and have created the following file db/migrate/20200820171322_change_default_field_on_table.rb
# frozen_string_literal: true
class ChangeDefaultFieldOnTable < ActiveRecord::Migration[5.1]
def up
change_column_default(:table, :column_name, nil)
end
end
That should pretty much do the trick, in case it helps!

Add Rows on Migrations

I'd like to know which is the preferred way to add records to a database table in a Rails Migration. I've read on Ola Bini's book (Jruby on Rails) that he does something like this:
class CreateProductCategories < ActiveRecord::Migration
#defines the AR class
class ProductType < ActiveRecord::Base; end
def self.up
#CREATE THE TABLES...
load_data
end
def self.load_data
#Use AR object to create default data
ProductType.create(:name => "type")
end
end
This is nice and clean but for some reason, doesn't work on the lasts versions of rails...
The question is, how do you populate the database with default data (like users or something)?
Thanks!
The Rails API documentation for migrations shows a simpler way to achieve this.
http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
class CreateProductCategories < ActiveRecord::Migration
def self.up
create_table "product_categories" do |t|
t.string name
# etc.
end
# Now populate the category list with default data
ProductCategory.create :name => 'Books', ...
ProductCategory.create :name => 'Games', ... # Etc.
# The "down" method takes care of the data because it
# drops the whole table.
end
def self.down
drop_table "product_categories"
end
end
Tested on Rails 2.3.0, but this should work for many earlier versions too.
You could use fixtures for that. It means having a yaml file somewhere with the data you want to insert.
Here is a changeset I committed for this in one of my app:
db/migrate/004_load_profiles.rb
require 'active_record/fixtures'
class LoadProfiles < ActiveRecord::Migration
def self.up
down()
directory = File.join(File.dirname(__FILE__), "init_data")
Fixtures.create_fixtures(directory, "profiles")
end
def self.down
Profile.delete_all
end
end
db/migrate/init_data/profiles.yaml
admin:
name: Admin
value: 1
normal:
name: Normal user
value: 2
You could also define in your seeds.rb file, for instance:
Grid.create :ref_code => 'one' , :name => 'Grade Única'
and after run:
rake db:seed
your migrations have access to all your models, so you shouldn't be creating a class inside the migration.
I am using the latest rails, and I can confirm that the example you posted definitely OUGHT to work.
However, migrations are a special beast. As long as you are clear, I don't see anything wrong with an ActiveRecord::Base.connection.execute("INSERT INTO product_types (name) VALUES ('type1'), ('type2')").
The advantage to this is, you can easily generate it by using some kind of GUI or web front-end to populate your starting data, and then doing a mysqldump -uroot database_name.product_types.
Whatever makes things easiest for the kind of person who's going to be executing your migrations and maintaining the product.
You should really not use
ProductType.create
in your migrations.
I have done similar but in the long run they are not guaranteed to work.
When you run the migration the model class you are using is the one at the time you run the migration, not the one at the time you created the migration. You will have to be sure you never change your model in such a way to stop you migration from running.
You are much better off running SQL for example:
[{name: 'Type', ..}, .. ].each do |type|
execute("INSERT INTO product_types (name) VALUES ('#{type[:name]} .. )
end

Resources