12

In my app I have teams and each team has a game time every week. I want the game times to be set to 'now' as a default. My table is set up like so

create_table "teams", force: true do |t|
  t.datetime "wk1_time"
end

I created a migration and it looks like this:

class ChangeDateTimeDefault < ActiveRecord::Migration
  def change
    change_column :teams, :wk1_time, :default => DateTime.now
  end
end

When I run rake db:migrate I get an error. Is my syntax wrong or am I missing something else?

Marco Prins
  • 7,189
  • 11
  • 41
  • 76
Phil Mok
  • 3,860
  • 6
  • 24
  • 36

6 Answers6

19

Since Rails 5 you can make a migration like this:

change_column_default :users, :wk1_time, -> { 'CURRENT_TIMESTAMP' }

In my mind this is the best option because it not database specific answer.

alexandre-rousseau
  • 2,321
  • 26
  • 33
11

Yes, you are missing the type :

class ChangeDateTimeDefault < ActiveRecord::Migration
  def change
    change_column :teams, :wk1_time, :datetime, :default => DateTime.now
  end
end

But, you need the below not the above one, because you just want to change the default.

class ChangeDateTimeDefault < ActiveRecord::Migration
  def change
    change_column_default :teams, :wk1_time, DateTime.now
  end
end

But none of these are correct approach for your task. The reason is DateTime.now will be evaluated based upon when you ran the migration, instead when the record is created. You need look to into this answer to know how to set the default time.

Community
  • 1
  • 1
Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317
8

EDIT: For Rails 5+ there are better answers, like this one: https://stackoverflow.com/a/55357711/252799, though the below still works.

The way I found, was to do a migration on an existing datetime column, like this:

#migration
execute("ALTER TABLE teams ALTER COLUMN wk1_time SET DEFAULT CURRENT_TIMESTAMP")

that produces a schema.rb entry shown like this:

#schema.rb
t.datetime "wk1_time",                    default: "now()", null: false

The "now()" is a string sent to postgresql and evaluated at runtime, upon each insert.

oma
  • 38,642
  • 11
  • 71
  • 99
  • 2
    If you downvote, please tell me why, this is a forum for education. I'll treat you with respect, I know I'm not always right. The solution presented here, worked for us, I am not pulling it as long as I think it's helpful to others. – oma Jan 17 '18 at 10:02
  • 3
    FWIW, at least on Rails 6, this doesn't work because it's not in a lambda. To make this work it needs to be: `default: -> { "now()" }` – kroehre Oct 06 '20 at 19:25
  • 1
    @kroehre The `"now()"` is evaluated in PG, not Ruby. The answer still works as it isn't dependent on any Rails version, though in Rails 5 this can be done Ruby-side: https://stackoverflow.com/a/40647191/252799 – oma Oct 09 '20 at 19:11
  • 1
    This didn't work for me without the lambda in Rails 6. The column ended up without any default and it raised an error complaining about null values. Adding the lambda fixed the issue. – lobati Feb 10 '21 at 19:32
  • a lambda to the `execute` doesn't make sense... you probably misunderstood and put the schema result into a migration. – oma Feb 12 '21 at 04:29
4

You're going to run into problems settings the default date time in the migration. This is because DateTime.now will be evaluated based upon when the migrate runs, not when the record is created!

To fix that you'll need to create an ActiveRecord callback in order to set wk1_time like so:

before_create :set_default_wk1_datetime
def set_default_wk1_datetime
  self.wk1_time = DateTime.now
end
Gavin Miller
  • 43,168
  • 21
  • 122
  • 188
1

for Postgresql :

add_column :users, :msgs_seen_at, 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'

but you'll have to use user.reload after user = User.create in order to "see" msgs_seen_at

Nicolas Maloeuvre
  • 3,069
  • 24
  • 42
-1

This works for me:

class MigrationName < ActiveRecord::Migration[7.0]
  def change
    add_column :table_name, :column_name, :integer, default: 100, null: false
    add_column :table_name, :column_name_date_to_save, :datetime, default: DateTime.now
  end
end
  • Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? **If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient.** Can you kindly [edit] your answer to offer an explanation? – Jeremy Caney Jun 29 '23 at 19:16