Как установить значение по умолчанию для столбца datetime для записи времени создания при миграции?

114

Рассмотрим сценарий создания таблицы ниже:

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

Можно ли установить значение по умолчанию как текущее время?

Я пытаюсь найти независимый от БД эквивалент в рельсах для определений столбцов SQL, приведенных ниже:

Синтаксис 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
Я добавил что-то вроде этого: add_column :table_name, :start_date, :datetime, default: -> { 'CURRENT_TIMESTAMP' }и вот как это выглядит в schema.rb. t.datetime "start_date", default: -> { "now()" }Но когда я создал новую запись, она не заполнялась . Есть идеи, почему?
Sandip Subedi
@SandipSubedi мне тоже. По рельсам 5.2.3.
courtimas 05
1
@courtsimas вам нужно сделать, post.reloadчтобы получить эти значения. Это объясняется здесь: stackoverflow.com/questions/53804787/…
Sandip Subedi
1
@SandipSubedi Я это понял через 5 минут после моего комментария. Так же, как с генерацией uuid. Спасибо!
courtimas 05
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, но не нашел решения. Я подумал, что попрошу всех посмотреть, есть ли альтернативы.
Хариш Шетти,
Я бы предложил сделать это с помощью обратного вызова before_create.
jonnii
1
Я не хочу изменять таблицу БД, так как хочу сохранить нейтральную базу данных кода. Я надеялся, что у AR есть какой-то механизм для установки значения по умолчанию для поля Datetime, аналогичного полю created_at.
Хариш Шетти,
9
Имейте в виду, что обратный вызов before_create выполняется перед вставкой в ​​базу данных, но после того, как вы создадите экземпляр своего объекта (например, с new()). Так self.foo = Time.nowбудет отменено значение, которое вы могли бы дать new(). Я предлагаю self.foo = Time.current unless self.foo.present?вместо этого.
Фатих
2
Не то, чтобы при использовании выполнения это помещало строку :starts_at, :default => 'now()'в schema.rb. Однако это не сработает при использовании, rake db:schema:dumpкоторый переопределит schema.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 для установки текущей даты. Если я часто сталкиваюсь с этим сценарием, мне приходится прибегать к написанию плагина, чтобы изменить обработку значений по умолчанию в методе to_sql класса ColumnDefinition.
Хариш Шетти,
10

Я обычно делаю:

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
9

Я искал похожие решения, но закончил использовать https://github.com/FooBarWidget/default_value_for .

default_value_forПлагин позволяет определить значения по умолчанию для моделей ActiveRecord в заявительном порядке. Например:

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
Джованни Каппеллотто
источник
8

Если вам нужно изменить в существующий столбец DateTime в Rails 5 (вместо создания новой таблицы , как указано в других ответах) , так что он может воспользоваться возможностью даты по умолчанию, вы можете создать миграцию , как это:

class MakeStartsAtDefaultDateForFoo < ActiveRecord::Migration[5.0]
  def change
    change_column :foos, :starts_at, :datetime, default: -> { 'CURRENT_TIMESTAMP' }
  end
end
Мэтт Лонг
источник
Плохой тон голосовать против и не объяснять, на какой вопрос был дан ответ. Кто-нибудь знает, почему это было отклонено? Он отображает синтаксис, если вы хотите изменить столбец вместо его создания.
Мэтт Лонг,
Я не тот, кто проголосовал против, но мне трудно понять, чем этот ответ отличается от ответа завещания, который предшествует вашему на один год. Вопрос в том, чтобы установить значение по умолчанию, и ваш ответ содержит такое же лямбда-предложение.
nurettin
2
@nurettin Я понимаю вашу точку зрения, но в свою защиту синтаксис для создания нового столбца несколько отличается от изменения существующего столбца. Предоставление этого синтаксиса тем, кто нашел этот вопрос с помощью веб-поиска при попытке добавить значение по умолчанию к своей текущей модели данных, а не создавать новую модель / таблицу в целом, вероятно, очень полезно. Нет? Вы предполагаете, что все знают, что они могут использовать одну и ту же лямбду для change_column. Может быть, им стоит это понять, но именно поэтому я ответил здесь - чтобы им не нужно было никуда идти, чтобы понять это. Ура!
Мэтт Лонг
4
FWIW Я только что использовал этот синтаксис и ценю, что для моего поиска в Google и конкретной проблемы этот ответ помог мне больше всего.
Джей Киллин
1
Я не вижу ничего плохого в том, что это ответ, а не комментарий. Upvoted. Я думаю, что отрицатели просто читают небрежно (как и большинство ответственных за вопросы!; P).
iconoclast
-1

В ответе @ szymon-lipiński (Szymon Lipiński) метод выполнения у меня не сработал. Выдавала синтаксическую ошибку 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
Сони Мэтью
источник