Додайте часові позначки до наявної таблиці


173

Мені потрібно додати часові позначки ( created_at& updated_at) до існуючої таблиці. Я спробував наступний код, але він не спрацював.

class AddTimestampsToUser < ActiveRecord::Migration
    def change_table
        add_timestamps(:users)
    end
end

Відповіді:


211

Помічник часової позначки доступний лише у create_tableблоці. Ви можете додати ці стовпці, вказавши типи стовпців вручну:

class AddTimestampsToUser < ActiveRecord::Migration
  def change_table
    add_column :users, :created_at, :datetime, null: false
    add_column :users, :updated_at, :datetime, null: false
  end
end

Хоча це не має такого ж короткого синтаксису, як add_timestampsметод, який ви вказали вище, Rails все одно буде розглядати ці стовпці як стовпчики часових позначок і нормально оновлювати значення.


10
Це не спрацювало для мене в Rails 4. Наведене нижче рішення "mu є занадто коротким" працює.
newUserNameHere

21
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime- ярлик для створення міграції вище.
Костянтин Калбазов

2
Запуск цієї міграції призводить до помилок, PG::NotNullViolation: ERROR: column "created_at" contains null value оскільки моя таблиця вже містить дані, що порушують ненулеві обмеження. Будь-який кращий спосіб зробити це, ніж спочатку видалити ненульовий контрант, а потім додати його згодом?
М. Хабіб

1
@ M.Habib Я не думаю, що це, але ця відповідь добре приєднує все до однієї міграції.
маленький ліс

1
@ M.Habib залежить від того, що, на вашу думку, має найбільш сенс для значення за замовчуванням, яке ви могли б зробити add_column :users, :updated_at, :datetime, null: false, default: Time.zone.now. Time.zone.nowце лише приклад, ви повинні використовувати будь-яке значення, яке має сенс для вашої логіки.
Делонг Гао

91

Міграція - це лише два методи класу (або методи екземпляра в 3.1): upі down(а іноді changeметод екземпляра в 3.1). Ви хочете, щоб ваші зміни перейшли до upметоду:

class AddTimestampsToUser < ActiveRecord::Migration
  def self.up # Or `def up` in 3.1
    change_table :users do |t|
      t.timestamps
    end
  end
  def self.down # Or `def down` in 3.1
    remove_column :users, :created_at
    remove_column :users, :updated_at
  end
end

Якщо вам 3,1, ви також можете використовувати change(дякую Дейву):

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table(:users) { |t| t.timestamps }
  end
end

Можливо , ти плутаєш def change, def change_tableі change_table.

Детальнішу інформацію див. У посібнику з міграції .


1
(Ну, тут є changeметод зараз, хоча в цьому випадку не проблема :)
Дейв Ньютон,

@Dave: Досить правда, я пішов на загальне, щоб уникнути проблем з версією, але changeварто згадати, тому додам і цього.
mu занадто короткий,

Правда, але я чув, що це дійсно змінюється з 3.1 і "вниз" дійсно йде. Рейки для автоматичного визначення методу вниз. Ви чули про це?
Майкл Дюрант

@Michael: Я використовував MongoDB виключно за допомогою програми 3.1, над якою працюю, тому я не працював з міграцією 3.1 AR. Документи вказують, що все рухається до методів екземпляра (з невідомих причин).
mu занадто короткий,

@MichaelDurrant, існує багато сценаріїв, які "зміни" не охоплюють зараз, якщо вгору / вниз піде, з’являться деякі розлючені люди :) (додайте в зміну міграції пункт "якщо", щоб уникнути міграційних зіткнень, і спробуйте відкат назад ...) Навіть через 3 роки після того, як ви зробили цей коментар, я не думаю, що це змінюється. :)
frandroid

76

Ваш оригінальний код дуже близький праворуч, вам просто потрібно використовувати іншу назву методу. Якщо ви використовуєте Rails 3.1 або новішої версії, вам потрібно визначити changeметод замість change_table:

class AddTimestampsToUser < ActiveRecord::Migration
  def change
    add_timestamps(:users)
  end
end

Якщо ви використовуєте старішу версію, вам потрібно визначитись upі downметоди замість change_table:

class AddTimestampsToUser < ActiveRecord::Migration
  def up
    add_timestamps(:users)
  end

  def down
    remove_timestamps(:users)
  end
end

59

@ user1899434 відповідь сприйняла той факт, що "існуюча" таблиця тут може означати таблицю з записами, які вже є в ній, записи, які ви, можливо, не хочете скидати. Тож коли ви додаєте часові позначки з null: false, що є типовим і часто бажаним, ці існуючі записи недійсні.

Але я думаю, що відповідь можна покращити, поєднавши два етапи в одну міграцію, а також за допомогою більш семантичного методу add_timestamps:

def change
  add_timestamps :projects, default: Time.zone.now
  change_column_default :projects, :created_at, nil
  change_column_default :projects, :updated_at, nil
end

Ви можете замінити якусь іншу часову позначку DateTime.now, наприклад, якщо ви хочете створити / оновити попередні записи на світанку часу.


2
Дивовижний. Дякую! Лише одна примітка - Time.zone.nowце те, що слід використовувати, якщо ми хочемо, щоб наш код відповідав правильному часовому поясу.
Джон Галлахер

4
Існує проблема із встановленням за замовчуванням, до Time.zone.nowякого він поверне екземпляр Time, який створюється під час виконання міграції, і просто використовувати цей час як за замовчуванням. Нові об'єкти не отримають новий екземпляр Time.
Тові Ньюмен

38
class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table :users do |t|
      t.timestamps
    end
  end
end

Доступні перетворення є

change_table :table do |t|
  t.column
  t.index
  t.timestamps
  t.change
  t.change_default
  t.rename
  t.references
  t.belongs_to
  t.string
  t.text
  t.integer
  t.float
  t.decimal
  t.datetime
  t.timestamp
  t.time
  t.date
  t.binary
  t.boolean
  t.remove
  t.remove_references
  t.remove_belongs_to
  t.remove_index
  t.remove_timestamps
end

http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html


10

Відповідь Ніка Девіса є найбільш повною з точки зору додавання стовпців часових позначок до таблиці з наявними даними. Єдиним його недоліком є ​​те, що він підніметься ActiveRecord::IrreversibleMigrationна a db:rollback.

Його слід модифікувати так, щоб він працював в обох напрямках:

def change
  add_timestamps :campaigns, default: DateTime.now
  change_column_default :campaigns, :created_at, from: DateTime.now, to: nil
  change_column_default :campaigns, :updated_at, from: DateTime.now, to: nil
end

Це не спрацювало так, як написано для мене на Rails 4.2.7 (я думаю change_column_default, не підтримує fromі toв цій версії?), Але я взяв цю ідею і створив up/downметоди замість одного changeметоду, і це спрацювало як шарм!
гар


4

не впевнений, коли саме це було введено, але в рейках 5.2.1 ви можете це зробити:

class AddTimestampsToMyTable < ActiveRecord::Migration[5.2]
  def change
    add_timestamps :my_table
  end
end

Докладніше див. " використання методу зміни " в документах про активні міграції записів.


Я не змусив його працювати з міграцією [5.1]; потім я змінив число на [5.2], і Rails сказав мені, що я можу використовувати лише 5.1, 5.0 або 4.2. Я пробував з 5.0 без успіху, а потім у 4.2 з успіхом.
Ма

Старий, я знаю, але якщо у вас є існуючі записи додати: , null: trueпісля:my_table
Jomar

2

Я створив просту функцію, яку можна зателефонувати, щоб додати до кожної таблиці (якщо матимете наявну базу даних) поля create_at та updated_at :

  # add created_at and updated_at to each table found.
  def add_datetime
    tables = ActiveRecord::Base.connection.tables
    tables.each do |t|
      ActiveRecord::Base.connection.add_timestamps t  
    end    
  end

2

add_timestamps (ім'я таблиці, параметри = {}) загальнодоступні

Додає стовпчики часу (створені_at та оновлені_at) стовпці. Додаткові параметри (як null: false) пересилаються до #add_column.

class AddTimestampsToUsers < ActiveRecord::Migration
  def change
    add_timestamps(:users, null: false)
  end
end

1

Відповіді раніше здаються правильними, проте я зіткнувся з проблемами, якщо в моїй таблиці вже є записи.

Я отримав би "ПОМИЛКА: стовпець created_atмістить nullзначення".

Для виправлення я використав:

def up
  add_column :projects, :created_at, :datetime, default: nil, null: false
  add_column :projects, :updated_at, :datetime, default: nil, null: false
end

Потім я використовував gem migra_data, щоб додати час для поточних проектів з міграції, таких як:

def data
  Project.update_all created_at: Time.now
end

Тоді всі проекти, створені після цієї міграції, будуть правильно оновлені. Переконайтеся, що сервер також перезапущений, щоб Rails ActiveRecordпочав відслідковувати часові позначки на записі.


1

Тут дуже багато відповідей, але я також розміщу свої, тому що жоден з попередніх не працював для мене :)

Як зазначали деякі, #add_timestampsна жаль, додається null: falseобмеження, яке спричинить невірність старих рядків, оскільки ці значення не заповнюються. Більшість відповідей тут передбачають, що ми встановимо якесь значення за замовчуванням ( Time.zone.now), але я не хотів би цього робити, оскільки ці часові позначки за замовчуванням для старих даних не будуть правильними. Я не бачу значення в додаванні невірних даних до таблиці.

Тож моя міграція була просто:

class AddTimestampsToUser < ActiveRecord::Migration
  def change_table
    add_column :projects, :created_at, :datetime
    add_column :projects, :updated_at, :datetime
  end
end

Ні null: false, ніяких інших обмежень немає. Старі рядки продовжуватимуть діяти created_atяк як NULL, так і update_atяк NULL(до деякого оновлення для рядка). Нові ряди з’являться created_atта updated_atзаповнюються, як очікувалося.


1

Проблема з більшістю відповідей тут полягає в тому, що якщо за замовчуванням для Time.zone.nowвсіх записів буде мати час виконання міграції як їх час за замовчуванням, що, мабуть, не те, що ви хочете. У рейках 5 ви можете замість цього використовувати now(). Це встановить часові позначки для існуючих записів як час запуску міграції, так і час початку транзакції для здійснення знову вставлених записів.

class AddTimestampsToUsers < ActiveRecord::Migration def change add_timestamps :users, default: -> { 'now()' }, null: false end end


1

Використання Time.current- це гарний стиль https://github.com/rubocop-hq/rails-style-guide#timenow

def change
  change_table :users do |t|
    t.timestamps default: Time.current
    t.change_default :created_at, from: Time.current, to: nil
    t.change_default :updated_at, from: Time.current, to: nil
  end
end

або

def change
  add_timestamps :users, default: Time.current
  change_column_default :users, :created_at, from: Time.current, to: nil
  change_column_default :users, :updated_at, from: Time.current, to: nil
end

1

Це просте додавання часової позначки в існуючу таблицю.

class AddTimeStampToCustomFieldMeatadata < ActiveRecord::Migration
  def change
    add_timestamps :custom_field_metadata
  end
end

0

Для тих, хто не використовує Rails, але використовує activerecord, далі також додається стовпець до існуючої моделі, наприклад, для цілого поля.

ActiveRecord::Schema.define do
  change_table 'MYTABLE' do |table|
    add_column(:mytable, :my_field_name, :integer)
  end
end

0

Це changeне change_tableдля Rails 4.2:

class AddTimestampsToUsers < ActiveRecord::Migration
  def change
    add_timestamps(:users)
  end
end

0

Це виглядає як чисте рішення в Rails 5.0.7 (виявив метод change_column_null):

def change
  add_timestamps :candidate_offices, default: nil, null: true
  change_column_null(:candidate_offices, :created_at, false, Time.zone.now)
  change_column_null(:candidate_offices, :created_at, false, Time.zone.now)
end

0

Я на рейках 5.0 і жоден із цих варіантів не працював.

Єдине, що працювало, було використовувати тип, який буде: timetamp, а не: datetime

def change
    add_column :users, :created_at, :timestamp
    add_column :users, :updated_at, :timestamp
end

-1

Я особисто використовував таке, і він оновлював усі попередні записи з поточним часом / датою:

add_column :<table>, :created_at, :datetime, default: Time.zone.now, null: false
add_column :<table>, :updated_at, :datetime, default: Time.zone.now, null: false

-2

Я зіткнувся з тим же питанням на Rails 5, намагаючись використовувати

change_table :my_table do |t|
    t.timestamps
end

Мені вдалося додати стовпчики часових позначок вручну за допомогою наступного:

change_table :my_table do |t|
    t.datetime :created_at, null: false, default: DateTime.now
    t.datetime :updated_at, null: false, default: DateTime.now
end

Чи не завжди це встановлюватиме значення за замовчуванням за часом у момент запуску міграції? (тож насправді не динамічна мітка часу, якою керує БД)
Гійом Петі

для записів, які вже існують у вашому db, так, він встановить create_at та updated_at до дати, коли міграція була запущена. Не маючи заздалегідь цих значень, іди, як ще ви ініціалізували ці значення. EDIT: Це було б просто вважати початком історії цього ряду
Андрес Розалес
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.