165

I've seen a few questions (namely this one) here on SO about adding a default boolean value to an existing column. So I tried the change_column suggestion but I mustn't be doing it right.

I tried:

$ change_column :profiles, :show_attribute, :boolean, :default => true

Which returns -bash: change_column: command not found

I then ran:

$ rails g change_column :profiles, :show_attribute, :boolean, :default => true

...and

$ rails change_column :profiles, :show_attribute, :boolean, :default => true

Then ran rake db:migrate, but the value for :show_attribute remained nil. In the question I referenced above it says in PostgreSQL you need to update it manually. Since I'm using PostgreSQL I added the following in my create_profiles migration:

t.boolean :show_attribute, :default => true

Can someone tell me what I'm doing wrong here?

Community
  • 1
  • 1
tvalent2
  • 4,959
  • 10
  • 45
  • 87

7 Answers7

323

change_column is a method of ActiveRecord::Migration, so you can't call it like that in the console.

If you want to add a default value for this column, create a new migration:

rails g migration add_default_value_to_show_attribute

Then in the migration created:

# That's the more generic way to change a column
def up
  change_column :profiles, :show_attribute, :boolean, default: true
end

def down
  change_column :profiles, :show_attribute, :boolean, default: nil
end

OR a more specific option:

def up
    change_column_default :profiles, :show_attribute, true
end

def down
    change_column_default :profiles, :show_attribute, nil
end

Then run rake db:migrate.

It won't change anything to the already created records. To do that you would have to create a rake task or just go in the rails console and update all the records (which I would not recommend in production).

When you added t.boolean :show_attribute, :default => true to the create_profiles migration, it's expected that it didn't do anything. Only migrations that have not already been ran are executed. If you started with a fresh database, then it would set the default to true.

Robin
  • 21,667
  • 10
  • 62
  • 85
  • 2
    That change_column call should be in the `up` method in the migration, which is a new class that will be generated in db/migrate/. (The `down` method should be written to undo what `up` does.) Make that change, then `rake db:migrate`. – rkb Dec 24 '11 at 22:24
  • it wasn't working for me until I wrote `def self.up` and `def self.down` – Kamil Szot Mar 04 '13 at 14:16
  • You're probably using an older version of rails then. I think this syntax is there since 3.1. – Robin Mar 04 '13 at 14:24
  • And in Rails 5, you leave off the _attribute so it should just say `show` or whatever the column name is. – labyrinth Mar 06 '18 at 16:04
96

As a variation on the accepted answer you could also use the change_column_default method in your migrations:

def up
  change_column_default :profiles, :show_attribute, true
end

def down
  change_column_default :profiles, :show_attribute, nil
end

Rails API-docs

Mariano Cavallo
  • 950
  • 11
  • 16
Sebastiaan Pouyet
  • 1,346
  • 9
  • 10
  • 1
    This ensures you won't accidentally change any of the other column properties – Brian Low Jul 27 '15 at 16:09
  • 1
    And in Rails 5 you leave off the _attribute so it should just say `show` or whatever the column name is. – labyrinth Mar 06 '18 at 16:05
  • 1
    @labyrinth What do you mean? `show_attribute` *is* the name of the column, I don't think rails 5 has anything to do with that, right? – Robin May 29 '19 at 15:53
37

I'm not sure when this was written, but currently to add or remove a default from a column in a migration, you can use the following:

change_column_null :products, :name, false

Rails 5:

change_column_default :products, :approved, from: true, to: false

http://edgeguides.rubyonrails.org/active_record_migrations.html#changing-columns

Rails 4.2:

change_column_default :products, :approved, false

http://guides.rubyonrails.org/v4.2/active_record_migrations.html#changing-columns

Which is a neat way of avoiding looking through your migrations or schema for the column specifications.

Clamoris
  • 437
  • 3
  • 12
fbelanger
  • 3,522
  • 1
  • 17
  • 32
  • Beware, it's from Rails 5 documentation. Rails 4.2 version of this does not accept hash but exactly new default as the third parameter. http://guides.rubyonrails.org/v4.2/active_record_migrations.html#changing-columns – Clamoris Jul 22 '16 at 14:11
  • About Rails 5, doing both seems to be the most correct way, e.g. `null: false` and `default: :something` basically – Dorian Jun 05 '17 at 22:49
2

If you just made a migration, you can rollback and then make your migration again.

To rollback you can do as many steps as you want:

rake db:rollback STEP=1

Or, if you are using Rails 5.2 or newer:

rails db:rollback STEP=1

Then, you can just make the migration again:

def change
  add_column :profiles, :show_attribute, :boolean, default: true
end

Don't forget to rake db:migrate and if you are using heroku heroku run rake db:migrate

B-M
  • 1,248
  • 9
  • 19
1

Also, as per the doc:

default cannot be specified via command line

https://guides.rubyonrails.org/active_record_migrations.html

So there is no ready-made rails generator. As specified by above answers, you have to fill manually your migration file with the change_column_default method.

You could create your own generator: https://guides.rubyonrails.org/generators.html

Margotte
  • 101
  • 1
  • 5
0
change_column :things, :price_1, :integer, default: 123, null: false

Seems to be best way to add a default to an existing column that doesn't have null: false already.

Otherwise:

change_column :things, :price_1, :integer, default: 123

Some research I did on this:

https://gist.github.com/Dorian/417b9a0e1a4e09a558c39345d50c8c3b

Dorian
  • 22,759
  • 8
  • 120
  • 116
0

If you don't want to create another migration-file for a small, recent change - from Rails Console:

ActiveRecord::Migration.change_column :profiles, :show_attribute, :boolean, :default => true

Then exit and re-enter rails console, so DB-Changes will be in-effect. Then, if you do this ...

Profile.new()

You should see the "show_attribute" default-value as true.

For existing records, if you want to preserve existing "false" settings and only update "nil" values to your new default:

Profile.all.each{|profile| profile.update_attributes(:show_attribute => (profile.show_attribute == nil ? true : false))  }

Update the migration that created this table, so any future builds of the DB will get it right from the onset. Also run the same process on any deployed-instances of the DB.

If using the "new db migration" method, you can do the update of existing nil-values in that migration.

JosephK
  • 668
  • 10
  • 18