Добавить метки времени в существующую таблицу

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 is too short" работает.
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 Я так не думаю, но этот ответ прекрасно объединяет все это в одну миграцию.
littleforest
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стоит упомянуть, поэтому я добавлю это тоже.
мю слишком коротка
Правда мю, но я слышал, что это действительно меняется с 3,1, и «вниз» действительно уходит. Rails, чтобы выяснить метод down автоматически. Вы слышали об этом?
Майкл Даррант
@Michael: Я использую MongoDB исключительно с приложением 3.1, над которым я работаю, поэтому я не работал с миграциями 3.1 AR. Документы указывают, что все движется в сторону методов экземпляра (по неизвестным причинам).
мю слишком коротка
@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
georgebrock
источник
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, созданный при запуске миграции, и просто использовать это время по умолчанию. Новые объекты не получат новый экземпляр времени.
Тови Ньюман
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на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
lightyrs
источник
Это не сработало точно так, как написано для меня на Rails 4.2.7 (я думаю, change_column_defaultчто не поддерживает fromи toв этой версии?), Но я взял эту идею и создал up/downметоды вместо одного changeметода, и это сработало как шарм!
гар
4

не уверен, когда именно это было введено, но в rails 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 (table_name, options = {}) public

Добавляет столбцы временных меток (созданный и обновленный_кат) в имя_таблицы. Дополнительные параметры (например, null: false) пересылаются в #add_column.

class AddTimestampsToUsers < ActiveRecord::Migration
  def change
    add_timestamps(:users, null: false)
  end
end
almawhoob
источник
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

Затем я использовал драгоценные migration_data , чтобы добавить время для текущих проектов по миграции , такие как:

def data
  Project.update_all created_at: Time.now
end

Тогда все проекты, созданные после этой миграции, будут корректно обновлены. Убедитесь, что сервер тоже перезапущен, чтобы Rails ActiveRecordначал отслеживать временные метки в записи.

dbrody
источник
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как as NULLи update_atas NULL(пока не будет выполнено какое-либо обновление строки). Новые строки будут иметь created_atи updated_atнаселен , как и ожидалось.

Kostis
источник
1

Проблема с большинством ответов здесь заключается в том, что если вы по умолчанию для Time.zone.nowвсех записей будете иметь время запуска миграции в качестве времени по умолчанию, что, вероятно, не то, что вы хотите. В рельсах 5 вы можете использовать вместо этого now(). Это установит временные метки для существующих записей как время выполнения миграции и как время начала транзакции фиксации для вновь вставленных записей.

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

jlesse
источник
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
shilovk
источник
1

Это простой способ добавить метку времени в существующую таблицу.

class AddTimeStampToCustomFieldMeatadata < ActiveRecord::Migration
  def change
    add_timestamps :custom_field_metadata
  end
end
Динеш Vaitage
источник
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 и ни один из этих вариантов не сработал.

Единственное, что сработало, это использование типа: timestamp, а не: 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
Jaime
источник
-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
Андрес Росалес
источник
не будет ли это всегда устанавливать значение по умолчанию со временем в момент запуска миграции? (так что на самом деле это не динамическая временная метка, обрабатываемая БД)
Гийом Пети
для записей, которые уже существуют в вашей базе данных, да, он установит в create_at и updated_at дату и время запуска миграции. Не имея заранее этих значений, подумайте, как иначе вы бы инициализировали эти значения. РЕДАКТИРОВАТЬ: Это будет считаться началом истории этого ряда
Андрес Росалес