Fügen Sie einer vorhandenen Tabelle Zeitstempel hinzu

173

Ich muss einer vorhandenen Tabelle Zeitstempel ( created_at& updated_at) hinzufügen . Ich habe den folgenden Code ausprobiert, aber er hat nicht funktioniert.

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

Antworten:

211

Der Zeitstempel-Helfer ist nur im create_tableBlock verfügbar . Sie können diese Spalten hinzufügen, indem Sie die Spaltentypen manuell angeben:

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

Dies hat zwar nicht die gleiche knappe Syntax wie die add_timestampsoben angegebene Methode, Rails behandelt diese Spalten jedoch weiterhin als Zeitstempelspalten und aktualisiert die Werte normal.

Ben Simpson
quelle
10
Dies hat bei mir in Rails 4 nicht funktioniert. Die folgende Lösung von "mu is too short" funktioniert.
newUserNameHere
21
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime- eine Verknüpfung zum Generieren der obigen Migration.
Konstantine Kalbazov
2
Das Ausführen dieser Migration führt zu Fehlern, PG::NotNullViolation: ERROR: column "created_at" contains null value da meine Tabelle bereits Daten enthält, die nicht gegen die Null-Einschränkung verstoßen. Gibt es eine bessere Möglichkeit, dies zu tun, als zuerst die Nicht-Null-Kontraint zu entfernen und sie später hinzuzufügen?
M. Habib
1
@ M.Habib Ich glaube nicht, aber diese Antwort fasst alles in einer Migration gut zusammen.
Littleforest
1
@ M.Habib hängt davon ab, was Ihrer Meinung nach für den Standardwert, den Sie tun können, am sinnvollsten ist add_column :users, :updated_at, :datetime, null: false, default: Time.zone.now. Time.zone.nowist nur ein Beispiel, Sie sollten jeden Wert verwenden, der für Ihre Logik sinnvoll ist.
Delong Gao
91

Migrationen sind nur zwei Klassenmethoden (oder Instanzmethoden in 3.1): upund down(und manchmal eine changeInstanzmethode in 3.1). Sie möchten, dass Ihre Änderungen in die upMethode übernommen werden:

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

Wenn Sie in 3.1 sind, können Sie auch verwenden change(danke Dave):

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

Vielleicht sind verwirrend Sie def change, def change_tableund change_table.

Weitere Informationen finden Sie im Migrationshandbuch .

mu ist zu kurz
quelle
1
(Nun, es gibt changejetzt die Methode, obwohl in diesem Fall nicht das Problem :)
Dave Newton
@ Dave: Richtig, ich habe mich für Generika entschieden, um die Versionsprobleme zu vermeiden, aber es changeist eine Erwähnung wert, also werde ich das auch hinzufügen.
Mu ist zu kurz
Richtig mu, aber ich habe gehört, dass sich das mit 3.1 wirklich ändert und das 'down' wirklich verschwindet. Schienen, um die Down-Methode automatisch herauszufinden. Hast du davon gehört?
Michael Durrant
@Michael: Ich habe MongoDB ausschließlich mit der 3.1-App verwendet, an der ich arbeite, also habe ich nicht mit 3.1-AR-Migrationen gearbeitet. Die Dokumente zeigen an, dass sich alles in Richtung Instanzmethoden bewegt (aus unbekannten Gründen).
Mu ist zu kurz
@MichaelDurrant, es gibt viele Szenarien, die "Änderung" derzeit nicht abdeckt. Wenn Auf / Ab weggeht, gibt es einige verärgerte Personen :) (Fügen Sie in Ihrer Änderungsmigration eine "Es sei denn" -Klausel hinzu, um Migrationskollisionen zu vermeiden, und versuchen Sie es Zurückrollen ...) Selbst 3 Jahre nachdem Sie diesen Kommentar abgegeben haben, glaube ich nicht, dass er sich ändert. :)
frandroid
76

Ihr ursprünglicher Code befindet sich ganz in der Nähe von rechts. Sie müssen lediglich einen anderen Methodennamen verwenden. Wenn Sie Rails 3.1 oder höher verwenden, müssen Sie eine changeMethode definieren , anstatt change_table:

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

Wenn Sie eine ältere Version verwenden, müssen Sie upund downMethoden definieren , anstatt change_table:

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

  def down
    remove_timestamps(:users)
  end
end
Georgebrock
quelle
58

Die Antwort von @ user1899434 hat die Tatsache aufgegriffen, dass eine "vorhandene" Tabelle hier eine Tabelle mit bereits darin enthaltenen Datensätzen bedeuten kann, Datensätze, die Sie möglicherweise nicht löschen möchten. Wenn Sie also Zeitstempel mit null: false hinzufügen, was die Standardeinstellung ist und häufig wünschenswert ist, sind alle vorhandenen Datensätze ungültig.

Ich denke jedoch, dass die Antwort verbessert werden kann, indem die beiden Schritte zu einer Migration kombiniert werden und die semantischere Methode add_timestamps verwendet wird:

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

Sie können einen anderen Zeitstempel ersetzen, z DateTime.now. B. wenn bereits vorhandene Datensätze zu Beginn der Zeit erstellt / aktualisiert werden sollen.

Nick Davies
quelle
2
Tolle. Danke dir! Nur eine Anmerkung - Time.zone.nowsollte verwendet werden, wenn unser Code der richtigen Zeitzone entsprechen soll.
John Gallagher
4
Es gibt ein Problem beim Festlegen der Standardeinstellung, bei Time.zone.nowder die beim Ausführen der Migration erstellte Zeitinstanz zurückgegeben wird und nur diese Zeit als Standard verwendet wird. Neue Objekte erhalten keine neue Zeitinstanz.
Tovi Newman
38
class AddTimestampsToUser < ActiveRecord::Migration
  def change
    change_table :users do |t|
      t.timestamps
    end
  end
end

Verfügbare Transformationen sind

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

Pradeep Sanjaya
quelle
10

Die Antwort von Nick Davies ist die vollständigste, wenn es darum geht, einer Tabelle mit vorhandenen Daten Zeitstempelspalten hinzuzufügen. Sein einziger Nachteil ist , dass es anheben wird ActiveRecord::IrreversibleMigrationauf einem db:rollback.

Es sollte so modifiziert werden, dass es in beide Richtungen funktioniert:

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
quelle
Dies funktionierte nicht genau so, wie es für mich in Rails 4.2.7 geschrieben wurde (ich glaube, change_column_defaultes wird nicht unterstützt fromund toin dieser Version?), Aber ich nahm diese Idee und erstellte up/downMethoden anstelle einer einzelnen changeMethode und es funktionierte wie ein Zauber!
Gar
8
def change
  add_timestamps :table_name
end
Ian Vaughan
quelle
4

Ich bin mir nicht sicher, wann genau dies eingeführt wurde, aber in Rails 5.2.1 können Sie dies tun:

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

Weitere Informationen finden Sie unter " Verwenden der Änderungsmethode " in den Dokumenten zur aktiven Datensatzmigration.

Riff Loretto
quelle
Ich habe es mit Migration nicht zum Laufen gebracht [5.1]; dann habe ich die Nummer auf [5.2] geändert und Rails sagte mir, dass ich nur 5.1, 5.0 oder 4.2 verwenden könnte. Ich habe es mit 5.0 ohne Erfolg versucht, dann in 4.2 mit Erfolg.
Ist Ma
Alt, ich weiß, aber wenn Sie vorhandene Aufzeichnungen haben, fügen Sie hinzu: , null: truenach dem:my_table
jomar
2

Ich habe eine einfache Funktion erstellt, die Sie aufrufen können, um jeder Tabelle (vorausgesetzt, Sie haben eine vorhandene Datenbank) die Felder created_at und updated_at hinzuzufügen :

  # 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
Roger
quelle
2

add_timestamps (table_name, options = {}) public

Fügt dem Tabellennamen Zeitstempelspalten (created_at und updated_at) hinzu. Zusätzliche Optionen (wie null: false) werden an #add_column weitergeleitet.

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

Die Antworten zuvor scheinen richtig zu sein, aber ich hatte Probleme, wenn meine Tabelle bereits Einträge enthält.

Ich würde 'ERROR: Spalte created_atenthält nullWerte' erhalten.

Um das Problem zu beheben, habe ich Folgendes verwendet:

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

Ich habe dann das Gem Migration_Data verwendet , um die Zeit für aktuelle Projekte für die Migration hinzuzufügen, z.

def data
  Project.update_all created_at: Time.now
end

Dann werden alle nach dieser Migration erstellten Projekte korrekt aktualisiert. Stellen Sie sicher, dass auch der Server neu gestartet wird, damit Rails ActiveRecorddie Zeitstempel im Datensatz verfolgt.

dbrody
quelle
1

Viele Antworten hier, aber ich werde auch meine posten, weil keine der vorherigen wirklich für mich funktioniert hat :)

Wie einige angemerkt haben, wird #add_timestampsleider die null: falseEinschränkung hinzugefügt, die dazu führt, dass alte Zeilen ungültig werden, da diese Werte nicht ausgefüllt sind. Die meisten Antworten hier legen nahe, dass wir einen Standardwert ( Time.zone.now) festlegen , aber ich würde das nicht gerne tun, da diese Standardzeitstempel für alte Daten nicht korrekt sind. Ich sehe den Wert nicht darin, der Tabelle falsche Daten hinzuzufügen.

Meine Migration war also einfach:

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

Nein null: false, keine weiteren Einschränkungen. Alte Zeilen bleiben mit created_atas NULLund update_atas weiterhin gültig NULL(bis eine Aktualisierung der Zeile durchgeführt wird). Neue Zeilen haben created_atund updated_atwie erwartet bevölkert.

Kostis
quelle
1

Das Problem mit den meisten Antworten hier ist, dass, wenn Sie standardmäßig Time.zone.nowalle Datensätze verwenden, die Zeit, zu der die Migration ausgeführt wurde, als Standardzeit angegeben wird, was wahrscheinlich nicht das ist, was Sie möchten. In Schienen 5 können Sie stattdessen verwenden now(). Dadurch werden die Zeitstempel für vorhandene Datensätze als Zeitpunkt der Ausführung der Migration und als Startzeit der Festschreibungstransaktion für neu eingefügte Datensätze festgelegt.

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

jlesse
quelle
1

Die Verwendung Time.currentist ein guter Stil 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

oder

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
quelle
1

Dies ist einfach, um einen Zeitstempel in eine vorhandene Tabelle einzufügen.

class AddTimeStampToCustomFieldMeatadata < ActiveRecord::Migration
  def change
    add_timestamps :custom_field_metadata
  end
end
Dinesh Vaitage
quelle
0

Für diejenigen, die keine Rails verwenden, aber activerecord verwenden, wird im Folgenden auch eine Spalte zu einem vorhandenen Modell hinzugefügt. Ein Beispiel ist ein ganzzahliges Feld.

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

Es ist changenicht change_tablefür Rails 4.2:

class AddTimestampsToUsers < ActiveRecord::Migration
  def change
    add_timestamps(:users)
  end
end
Igor T.
quelle
0

Dies scheint eine saubere Lösung in Rails 5.0.7 zu sein (die Methode change_column_null wurde entdeckt):

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
Wes Gamble
quelle
0

Ich bin auf Rails 5.0 und keine dieser Optionen hat funktioniert.

Das einzige, was funktionierte, war die Verwendung des Typs: Zeitstempel und nicht: Datum / Uhrzeit

def change
    add_column :users, :created_at, :timestamp
    add_column :users, :updated_at, :timestamp
end
Vishnu Narang
quelle
-1

Ich persönlich habe Folgendes verwendet und alle vorherigen Datensätze mit der aktuellen Uhrzeit / dem aktuellen Datum aktualisiert:

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
quelle
-2

Ich bin auf Rails 5 auf dasselbe Problem gestoßen, als ich versucht habe, es zu verwenden

change_table :my_table do |t|
    t.timestamps
end

Ich konnte die Zeitstempelspalten manuell wie folgt hinzufügen:

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
Andres Rosales
quelle
Wird dadurch nicht immer der Standardwert zum Zeitpunkt der Ausführung der Migration festgelegt? (also nicht wirklich ein dynamischer Zeitstempel, der von der DB verarbeitet wird)
Guillaume Petit
Ja, für die Datensätze, die bereits in Ihrer Datenbank vorhanden sind, werden das erstellte_at und das aktualisierte_at auf das Datum gesetzt, zu dem die Migration ausgeführt wurde. Ohne diese Werte vorher zu haben, fragen Sie, wie Sie diese Werte sonst initialisieren würden. EDIT: Es wäre nur der Beginn der Geschichte dieser Reihe
Andres Rosales