Як встановити значення за замовчуванням для стовпця часу для запису часу створення під час міграції?


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/isissue/27077 та відповідь на неї від prathamesh-sonpatki


14
Чудова відповідь. Будь ласка, пам’ятайте, що в Postgres CURRENT_TIMESTAMPнастане час початку поточної транзакції, тому кілька записів, створених в одній транзакції, отримають таке саме значення. Якщо ви хочете фактичний поточний час виконання оператора (ігноруючи контекст транзакції), ознайомтесь CLOCK_TIMESTAMP.
Abe Voelker

Я додав щось подібне: add_column :table_name, :start_date, :datetime, default: -> { 'CURRENT_TIMESTAMP' }і це виглядає так, як це виглядає в schema.rb t.datetime "start_date", default: -> { "now()" }Але коли я створив новий запис, він не заповнюється. Будь-яка ідея чому?
Сандіп

@SandipSubedi для мене те саме. На рейках 5.2.3.
Courtimas

1
@courtsimas, що вам потрібно зробити, post.reloadщоб отримати ці значення. Це пояснюється тут: stackoverflow.com/questions/53804787 / ...
Sandip Субедей

1
@SandipSubedi Я зрозумів, що через 5 хвилин після мого коментаря. Так само, як і ууїдового покоління. Дякую!
Courtimas

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

Я б запропонував зробити це з зворотним дзвінком до_створення
jonnii

1
Я не хочу змінювати таблицю БД, оскільки я хочу зберегти свій код DB нейтральним. Я сподівався, що в AR є якийсь механізм встановити значення за замовчуванням для поля Datetime, подібного до поля create_at.
Harish Shetty

9
Майте на увазі, що зворотний виклик before_create запускається перед вставкою в базу даних, але після того, як ви інстанціюєте об'єкт (наприклад, з new()). Таким чином, self.foo = Time.nowбуде перевершено значення, яке ви можете надати new(). Я пропоную self.foo = Time.current unless self.foo.present?замість цього.
Фатих

2
Не те, що при використанні Execute це поставить рядок :starts_at, :default => 'now()'у schema.rb. Однак це не буде працювати при використанні, rake db:schema:dumpяке буде заміняти schema.rb з :starts_at, :default => '2015-05-29 09:46:33'(або будь-якою датою, коли ви запускаєте сценарій) ... sad ....
astreal

13

Активні записи автоматично створюють та оновлюють часові позначки, якщо в таблиці є поля з назвою created_at/ created_onабо updated_at/ updated_on. Джерело - api.rubyonrails.org

Вам не потрібно робити нічого іншого, крім того, щоб мати цей стовпець.


Я вже маю ці поля у своїй таблиці. Мені потрібно додаткове поле для мого планувальника, щоб містити дату початку. В даний час я використовую: зворотний виклик before_create для встановлення поточної дати. Якщо я стикаюся з цим сценарієм часто, мені доведеться вдаватися до написання плагіна, щоб змінити обробку значень за замовчуванням у методі "to_sql" класу ColumnDefinition.
Харіш Шетті

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

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.
Альтер Лагос

8

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

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

Погана форма проголосувати щось проти, а не пояснити, яке питання було прийнято з відповіддю. Хтось має уявлення, чому це було проголошено? Він відображає синтаксис, якщо ви хочете змінити стовпець, а не створювати його.
Метт Лонг

Я не той, хто виступав проти цього, але мені важко бачити, як ця відповідь відрізняється від відповіді волі, яка передує вашій на рік. Питання полягає у встановленні за замовчуванням, а у вашій відповіді є те саме лямбда-застереження.
Нуреттін

2
@nurettin Я розумію, але на мій захист синтаксис створення нового стовпчика тонко відрізняється від зміни існуючого стовпця. Надання, що синтаксис для тих, хто знайшов це питання за допомогою веб-пошуку, намагаючись додати за замовчуванням до своєї поточної моделі даних, а не створювати нову модель / таблицю взагалі, мабуть, дуже корисний. Немає? Ви припускаєте, що всі знають, що вони можуть використовувати ту саму лямбда для стовпчика змін. Можливо, вони повинні це усвідомити, але саме тому я відповів тут, тому їм не доведеться більше нікуди їхати, щоб зрозуміти це. Ура!
Метт Лонг

4
FWIW Щойно я використав цей синтаксис і оцінюю, що для пошуку в Google і конкретної проблеми ця відповідь мені найбільше допомогла.
Джей Кілін

1
Я не бачу нічого поганого в тому, що це відповідь, а не коментар. Отримано. Я думаю, що низовики просто недбало читають (як і більшість закритих запитань !; p).
іконоборство

-1

У відповіді, яку дав @ szymon-lipiński (Szymon Lipiński), метод виконання для мене не працював. Це була помилка синтаксису MySQL.

Синтаксис MySQL, який працював для мене, це такий.

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

Отже, встановити значення за замовчуванням для стовпця часу в сценарії міграції можна зробити наступним чином:

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.