Rails - migrations - change_column_default - ruby-on-rails

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!

Related

How to make migration to update table RAILS

I am stuck with making migration which will update my table Users. I need to set country_code with 1 everywhere where i have "" or NULL for that column.
Thanks
class UpdateCountryCodeColumnUsers < ActiveRecord::Migration
def up
execute %Q(
UPDATE users
SET country_code = 1
WHERE country_code IS NULL OR country_code = ""
)
end
end
You probably shouldn't alter the data in a migration and only use it to alter the schema.
A lot of devs use rake db:reset which won't run this migration.
A better solution is to create a rake or thor task as a one off or simply just execute the SQL.
Sergei's answer is your best bet and will update the data (which would be needed first - and is very important) and it lets the database do the work. If you also need to set the default for going forward (after the update Sergei proposed) you can make a separate migration (to separate activities) and include the below...
You can also use the rails migration helper method change_column_default
change_column_default :users, :country_code, from: nil, to: 1
If you want to make it reversible just use change_column...
def up
change_column :users, :country_code, :string, default: 1
end
def down
change_column :users, :country_code, :string, default: nil
end
def UpdateCountryCodeForUsers < ActiveRecord::Migration
def up
Users.where("country_code = '' or country_code = NULL")
.update_attributes({country_code: 1})
end
end
On rails 5.2 , I needed to update some text attributes in a column & had to use update_all instead of update_attributes.
class UpdateTableColumn < ActiveRecord::Migration[5.2]
def up
TableName.where("my_column = 'my column old value'")
.update_all({keywords: 'my column new value'})
end
end

Rails migration not working

I have strange problem. This is my migration code:
class SetDefaultToFalse < ActiveRecord::Migration
def change
MyObject.where('done is ?', nil).each do |a|
a.done = false
a.save
end
end
end
If I run this on database dump from while ago, with all the other, older migrations, after it's done, any of the my_objects don't have done field marked as false. If I do rake db:rollback and db:migrate again, suddenly, it works. Why? Column done is added more then 5 migrations before this one. Changing previous migrations is not the solution i'm looking for.
You should have a look at this:
Setting different default values in rails migrations?
I am not sure why would you do that in a migration, but this might help:
MyObject.where('done is ?', nil).update_all(done: false)
done field added after create. In db schema 'done' field not exist.
Run
rake db:migrate
I suggest you try this code:
class SetDefaultToFalse < ActiveRecord::Migration
def self.up
MyObject.where('done is ?', nil).each do |a|
a.update_attribute(done: false)
end
end
end

Migrating DATA - not just schema, Rails

Sometimes, data migrations are required. As time passes, code changes and migrations using your domain model are no longer valid and migrations fail. What are the best practices for migrating data?
I tried make an example to clarify the problem:
Consider this. You have a migration
class ChangeFromPartnerAppliedToAppliedAt < ActiveRecord::Migration
def up
User.all.each do |user|
user.applied_at = user.partner_application_at
user.save
end
end
this runs perfectly fine, of course. Later, you need a schema change
class AddAcceptanceConfirmedAt < ActiveRecord::Migration
def change
add_column :users, :acceptance_confirmed_at, :datetime
end
end
class User < ActiveRecord::Base
before_save :do_something_with_acceptance_confirmed_at
end
For you, no problem. It runs perfectly. But if your coworker pulls both these today, not having run the first migration yet, he'll get this error on running the first migration:
rake aborted!
An error has occurred, this and all later migrations canceled:
undefined method `acceptance_confirmed_at=' for #<User:0x007f85902346d8>
That's not being a team player, he'll be fixing the bug you introduced. What should you have done?
This is a perfect example of the Using Models in Your Migrations
class ChangeFromPartnerAppliedToAppliedAt < ActiveRecord::Migration
class User < ActiveRecord::Base
end
def up
User.all.each do |user|
user.applied_at = user.partner_application_at
user.save
end
end
Edited after Mischa's comment
class ChangeFromPartnerAppliedToAppliedAt < ActiveRecord::Migration
class User < ActiveRecord::Base
end
def up
User.update_all('applied_at = partner_application_at')
end
end
Best practice is: don't use models in migrations. Migrations change the way AR maps, so do not use them at all. Do it all with SQL. This way it will always work.
This:
User.all.each do |user|
user.applied_at = user.partner_application_at
user.save
end
I would do like this
update "UPDATE users SET applied_at=partner_application_at"
Some times 'migrating data' could not be performed as a part of schema migration, like discussed above. Sometimes 'migrating data' means 'fix historical data inconstancies' or 'update your Solr/Elasticsearch' index, so its a complex task. For these kind of tasks, check out this gem https://github.com/OffgridElectric/rails-data-migrations
This gem was designed to decouple Rails schema migrations from data migrations, so it wont cause downtimes at deploy time and make it easy to manage in overall

Rails migration not working on heroku, Model class reported as uninitialized constant

I'm trying to run a migration on heroku and I can't seem to find the problem why my model class is not recognized.
This is my migration:
class AddTestToGoals < ActiveRecord::Migration
def change
add_column :goals, :test, :integer, default: 0, null: false
Goal.reset_column_information
Goal.all.each { |g| g.update_attribute :test, Goal::PASS }
end
end
Running it using
heroku run rake db:migrate
and I get this error
uninitialized constant AddTestToGoals::Goal
Anyone knows what the problem is?
EDIT: miss typed before, it's the model which is not recognized, not the constant in it.
HALF WORKAROUND:
Using this (which I found here: http://visibletrap.blogspot.co.il/2011/10/heroku-access-railss-model-in-migration.html)
class AddTestToGoals < ActiveRecord::Migration
class Goal < ActiveRecord::Base; end
def change
add_column :goals, :test, :integer, default: 0, null: false
Goal.reset_column_information
Goal.all.each { |g| g.update_attribute :test, Goal::PASS }
end
end
heroku doesn't complain about not knowing what Goal is which solves half of the problem.
but then, Goal::PASS is not recognized.
Old question, but I recently experienced something like it which was due to autoloading being disabled by setting
config.threadsafe!
in my environments/staging.rb file.
It can be fixed by replacing it with the following
config.threadsafe! unless $rails_rake_task
This should be OK as there is no need for rake tasks to be threadsafe.
EDIT:
Change all the references to Goal to be prefixed with ::.
::Goal.reset_column_information
::Goal.all.each { |g| g.update_attribute :test, ::Goal::PASS }
Unless I am missing something, all workarounds above have been blocked in later rails-versions. But one can still take control of their project/work from artificially-imposed constraints, by doing something like:
execute("INSERT INTO table_name (col_name) VALUES (your_values_here))")

Renaming table in 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)

Resources