마이그레이션에서 생성 시간을 기록하기 위해 datetime 열의 기본값을 설정하는 방법은 무엇입니까?


114

아래 테이블 생성 스크립트를 고려하십시오.

create_table :foo do |t|
  t.datetime :starts_at, :null => false
end

기본값을 현재 시간으로 설정할 수 있습니까?

아래에 주어진 SQL 열 정의에 대한 레일에서 DB 독립적 인 등가물을 찾으려고합니다.

Oracle 구문

start_at DATE DEFAULT SYSDATE() 

MySQL 구문

start_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP

또는

start_at DATETIME DEFAULT NOW()

답변:


174

이것은 이제 Rails 5에서 지원됩니다.

다음은 샘플 마이그레이션입니다.

class CreatePosts < ActiveRecord::Migration[5.0]
  def change
    create_table :posts do |t|
      t.datetime :modified_at, default: -> { 'CURRENT_TIMESTAMP' }
      t.timestamps
    end
  end 
end

https://github.com/rails/rails/issues/27077 에서 토론을 참조 하고 prathamesh-sonpatki의 답변


14
좋은 대답입니다. 순전히 제쳐두고 Postgres CURRENT_TIMESTAMP에서는 현재 트랜잭션이 시작되는 시간이 될 것이므로 동일한 트랜잭션에서 생성 된 여러 레코드가 동일한 값을 갖게됩니다. 문이 실행되는 실제 현재 시간 (트랜잭션 컨텍스트 무시)을 확인하려면을 확인하십시오 CLOCK_TIMESTAMP.
Abe Voelker 2017-10-08

나는 다음과 같은 것을 추가했다 : add_column :table_name, :start_date, :datetime, default: -> { 'CURRENT_TIMESTAMP' }이것이 schema.rb에서 보이는 것과 같다. t.datetime "start_date", default: -> { "now()" }하지만 새 레코드를 만들었을 때 채워지지 않았다. 왜 그런지 아세요?
Sandip Subedi

@SandipSubedi 나도 마찬가지입니다. 레일에서 5.2.3.
courtsimas

1
@courtsimas 당신 post.reload은 그 값을 얻기 위해해야 합니다. 여기에 설명되어 있습니다 : stackoverflow.com/questions/53804787/…
Sandip Subedi

1
@SandipSubedi 내 댓글 5 분 후에 깨달았습니다. uuid 생성과 마찬가지로. 감사!
courtsimas

119

다음과 같이 모델에 함수를 추가 할 수 있습니다.

  before_create :set_foo_to_now
  def set_foo_to_now
    self.foo = Time.now
  end

모델이 모델의 현재 시간을 설정하도록합니다.

또한 다음과 같이 데이터베이스 수준에서 기본값을 설정하기 위해 마이그레이션에 몇 가지 SQL 코드를 배치 할 수 있습니다.

execute 'alter table foo alter column starts_at set default now()'

다음과 같이 설정 :

create_table :foo do |t|
  t.datetime :starts_at, :null => false, :default => Time.now
end

마이그레이션하는 동안 Time.now 함수를 실행하므로 데이터베이스의 테이블이 다음과 같이 생성됩니다.

create table foo ( starts_at timestamp not null default '2009-01-01 00:00:00');

그러나 나는 그것이 당신이 원하는 것이 아니라고 생각합니다.


1
현재 : before_create 콜백에 값을 설정하고 있습니다. <br> 여기에서 일종의 AR 마법을 찾고있었습니다. Rails 코드를 살펴 보는데 시간을 보냈지 만 해결책을 찾지 못했습니다. 대안이 있는지 물어볼 것이라고 생각했습니다.
Harish Shetty

before_create에 대한 콜백으로 수행하는 것이 좋습니다.
jonnii

1
코드 DB를 중립적으로 유지하기 위해 DB 테이블을 변경하고 싶지 않습니다. AR에 created_at 필드와 유사한 Datetime 필드의 기본값을 설정하는 메커니즘이 있기를 바랐습니다.
Harish Shetty

9
주의하십시오 before_create 데이터베이스에 있지만 이후에 삽입하기 전에 콜백을 실행하면 (와 같은 개체를 인스턴스화 new()). 따라서 제공 self.foo = Time.now할 수있는 값을 재정의합니다 new(). self.foo = Time.current unless self.foo.present?대신 제안 합니다.
Fatih

2
실행을 사용할 때 :starts_at, :default => 'now()'schema.rb 에 한 줄이 추가됩니다 . 그러나 이것은 rake db:schema:dumpschema.rb를 재정의 할 때 작동하지 않습니다. :starts_at, :default => '2015-05-29 09:46:33'(또는 스크립트를 시작할 때 날짜에 관계없이) ... 슬프다 ....
astreal

13

Active Record는 테이블에 created_at/ created_on또는 updated_at/ 라는 필드가있는 경우 작업을 자동으로 생성 및 업데이트합니다 updated_on. 출처-api.rubyonrails.org

해당 열을 갖는 것 외에는 다른 작업을 수행 할 필요가 없습니다.


내 테이블에 이미 해당 필드가 있습니다. 스케줄러가 시작 날짜를 보유하려면 추가 필드가 필요합니다. 현재 : before_create 콜백을 사용하여 현재 날짜를 설정하고 있습니다. 이 시나리오가 자주 발생하면 ColumnDefinition 클래스의 'to_sql'메서드에서 처리하는 기본값을 변경하기 위해 플러그인을 작성해야합니다.
Harish Shetty

9

비슷한 솔루션을 찾고 있었지만 https://github.com/FooBarWidget/default_value_for 사용하여 끝났습니다 .

default_value_for플러그인은 하나가 선언적 방식으로 액티브 모델에 대한 기본 값을 정의 할 수 있습니다. 예를 들면 :

class User < ActiveRecord::Base
  default_value_for :name, "(no name)"
  default_value_for :last_seen do
    Time.now
  end
end

u = User.new
u.name       # => "(no name)"
u.last_seen  # => Mon Sep 22 17:28:38 +0200 2008

9

나는 보통 :

def change
  execute("
    ALTER TABLE your_table
    ALTER COLUMN your_column
    SET DEFAULT CURRENT_TIMESTAMP
  ")
end

그래서, 당신 schema.rb은 다음과 같은 것을 갖게 될 것입니다.

create_table "your_table", force: :cascade do |t|
  t.datetime "your_column", default: "now()"
end

단점 :이 솔루션은를 실행할 때만 작동 rake db:migrate하며 rake db:schema:load.
Alter Lagos

8

기본 날짜 기능을 활용할 수 있도록 Rails 5 의 기존 DateTime 열변경 해야하는 경우 (다른 답변에 지정된대로 새 테이블을 생성하는 대신) 다음과 같이 마이그레이션을 생성 할 수 있습니다.

class MakeStartsAtDefaultDateForFoo < ActiveRecord::Migration[5.0]
  def change
    change_column :foos, :starts_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
  end
end

투표를하지 않고 답변에 어떤 문제가 있는지 설명하지 않는 잘못된 형식입니다. 왜 이것이 투표에서 떨어졌는지 아는 사람이 있습니까? 열을 만드는 대신 열을 변경하려는 경우 구문을 표시합니다.
Matt Long

내가 반대표를 던진 사람은 아니지만,이 답변이 1 년 앞선 유언의 답변과 어떻게 다른지 잘 모르겠습니다. 질문은 기본값 설정에 관한 것이며 답변에는 동일한 람다 절이 있습니다.
nurettin

2
@nurettin 나는 당신의 요점을 이해하지만 내 방어에서 새 열 을 만드는 구문 은 기존 열 을 변경 하는 것과 미묘하게 다릅니다 . 웹 검색을 통해이 질문을 찾은 사람들에게 새 모델 / 테이블을 모두 만드는 대신 현재 데이터 모델에 기본값을 추가하려고 시도하는 사람들에게 해당 구문을 제공하는 것은 아마도 꽤 도움이 될 것입니다. 아니? 모두가 change_column에 동일한 람다를 사용할 수 있다는 것을 알고 있다고 가정합니다. 아마도 그들은 그것을 깨달아야 할 것입니다. 그러나 그것이 제가 여기서 대답 한 이유입니다. 그래서 그들은 그것을 알아 내기 위해 다른 곳으로 갈 필요가 없습니다. 건배!
Matt Long

4
FWIW 방금이 구문을 사용했으며 Google 검색 및 특정 문제에 대해이 답변이 가장 도움이되었음을 감사드립니다.
Jay Killeen

1
나는 이것이 코멘트 라기보다는 대답이라는 것에 잘못된 것이 없다고 생각합니다. 찬성. 나는 반대 투표자들이 부주의하게 읽고 있다고 생각합니다 (대부분의 질문이 닫힙니다!; p).
iconoclast

-1

@ szymon-lipiński (Szymon Lipiński)가 제공 한 대답에서 execute 메서드가 작동하지 않았습니다. MySQL 구문 오류가 발생했습니다.

나를 위해 일한 MySQL 구문은 이것입니다.

execute "ALTER TABLE mytable CHANGE `column_name` `column_name` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"

따라서 마이그레이션 스크립트에서 datetime 열의 기본값을 설정하려면 다음과 같이 할 수 있습니다.

def up
  create_table :foo do |t|
    t.datetime :starts_at, :null => false
  end

  execute "ALTER TABLE `foo` CHANGE `starts_at` `starts_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"
end
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.