Aggiungi timestamp a una tabella esistente


173

Devo aggiungere i timestamp ( created_at& updated_at) a una tabella esistente. Ho provato il seguente codice ma non ha funzionato.

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

Risposte:


211

L'helper timestamp è disponibile solo nel create_tableblocco. Puoi aggiungere queste colonne specificando manualmente i tipi di colonna:

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

Sebbene ciò non abbia la stessa sintassi concisa del add_timestampsmetodo sopra specificato, Rails continuerà a trattare queste colonne come colonne timestamp e aggiornerà i valori normalmente.


10
Questo non ha funzionato per me in Rails 4. La soluzione seguente di "mu is too short" sta funzionando.
— newUserNameQui

21
rails g migration AddTimestampsToUser created_at:datetime updated_at:datetime- un collegamento per generare la migrazione sopra.
— Konstantine Kalbazov,

2
L'esecuzione di questa migrazione provoca errori PG::NotNullViolation: ERROR: column "created_at" contains null value perché la mia tabella contiene già dati che violano il vincolo non nullo. Qual è il modo migliore per farlo che rimuovere inizialmente il contrappunto non nullo e poi aggiungerlo in seguito?
— M. Habib,

1
@ M.Habib Non la penso così, ma questa risposta racchiude bene tutto in un'unica migrazione.
— Foreste

1
@ M.Habib dipende da ciò che pensi abbia più senso per il valore predefinito, che potresti fare add_column :users, :updated_at, :datetime, null: false, default: Time.zone.now. Time.zone.nowè solo un esempio, dovresti usare qualunque valore abbia senso per la tua logica.
— Delong Gao,

91

Le migrazioni sono solo due metodi di classe (o metodi di istanza in 3.1): upe down(e talvolta un changemetodo di istanza in 3.1). Vuoi che le tue modifiche entrino nel upmetodo:

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

Se sei in 3.1, puoi anche usare change(grazie Dave):

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

Forse stai confondendo def change, def change_tablee change_table.

Consulta la guida alla migrazione per ulteriori dettagli.


1
(Bene, ora c'è il changemetodo, anche se in questo caso, non il problema :)
— Dave Newton,

@Dave: Abbastanza vero, sono andato per generico per evitare i problemi di versione, ma changevale la pena menzionare quindi aggiungerò anche quello.
— mu è troppo corto il

Vero mu, ma ho sentito che questo sta davvero cambiando con 3.1 e il 'down' sta davvero andando via. Rotaie per capire automaticamente il metodo down. Ne hai sentito parlare?
— Michael Durrant,

@Michael: sto usando MongoDB esclusivamente con l'app 3.1 su cui sto lavorando, quindi non ho lavorato con le migrazioni AR 3.1. I documenti indicano che tutto si sta muovendo verso metodi di istanza (per motivi sconosciuti).
— mu è troppo corto il

@MichaelDurrant, ci sono molti scenari che "il cambiamento" non copre in questo momento, se su / giù vanno via ci saranno alcune persone arrabbiate :) (aggiungi una clausola "a meno" nella tua migrazione di modifica per evitare collisioni di migrazione, e prova tornando indietro ...) Anche 3 anni dopo aver fatto questo commento, non penso che stia cambiando. :)
— Frandroid

76

Il tuo codice originale è molto vicino alla destra, devi solo usare un nome di metodo diverso. Se si utilizza Rails 3.1 o versioni successive, è necessario definire un changemetodo anziché change_table:

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

Se si utilizza una versione precedente, è necessario definire upe downmetodi anziché change_table:

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

  def down
    remove_timestamps(:users)
  end
end

59

La risposta di @utente1899434 è dovuta al fatto che una tabella "esistente" qui potrebbe significare una tabella con record già presenti, record che potresti non voler eliminare. Pertanto, quando si aggiungono i timestamp con null: false, che è l'impostazione predefinita e spesso auspicabile, i record esistenti non sono tutti validi.

Ma penso che la risposta possa essere migliorata, combinando i due passaggi in un'unica migrazione, e usando il metodo più semantico 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

Potresti sostituire qualche altro timestamp DateTime.now, come se volessi invece creare / aggiornare i record preesistenti all'alba dei tempi.


2
Sorprendente. Grazie! Solo una nota: Time.zone.nowè ciò che dovrebbe essere usato se vogliamo che il nostro codice rispetti il ​​fuso orario corretto.
— John Gallagher,

4
Si è verificato un problema con l'impostazione del valore predefinito in base al Time.zone.nowquale verrà restituita l'istanza di Time che viene creata quando viene eseguita la migrazione e utilizza tale tempo come predefinito. I nuovi oggetti non riceveranno una nuova istanza di Time.
— Tovi Newman,

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

Le trasformazioni disponibili sono

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

La risposta di Nick Davies è la più completa in termini di aggiunta di colonne timestamp a una tabella con dati esistenti. L'unico aspetto negativo è che rilancierà ActiveRecord::IrreversibleMigrationa db:rollback.

Dovrebbe essere modificato in questo modo per funzionare in entrambe le direzioni:

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

Questo non ha funzionato esattamente come scritto per me su Rails 4.2.7 (pensi change_column_defaultche non supporti frome toin quella versione?), Ma ho preso questa idea e ho creato up/downmetodi invece di un singolo changemetodo e ha funzionato come un fascino!
— Gar


4

non sono sicuro di quando sia stato introdotto esattamente questo, ma in Rails 5.2.1 puoi farlo:

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

per ulteriori informazioni, consultare " utilizzo del metodo di modifica " nei documenti sulle migrazioni dei record attivi.


Non l'ho fatto funzionare con Migration [5.1]; poi ho cambiato il numero in [5.2] e Rails mi ha detto che avrei potuto usare solo 5.1, 5.0 o 4.2. Ho provato con 5.0 senza successo, poi con 4.2 con successo.
— È Ma

Vecchio, lo so, ma se hai record esistenti aggiungi: , null: truedopo il:my_table
— jomar

2

Ho fatto una semplice funzione che è possibile chiamare da aggiungere a ciascun tavolo (supponendo che si dispone di un database esistente) il created_at e updated_at campi:

  # 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 = {}) pubblico

Aggiunge colonne timestamps (Created_at e Updated_at) a table_name. Le opzioni aggiuntive (come null: false) vengono inoltrate a #add_column.

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

1

Le risposte prima sembrano corrette, tuttavia ho riscontrato problemi se la mia tabella contiene già delle voci.

Vorrei ottenere 'ERRORE: la colonna created_atcontiene nullvalori'.

Per risolvere, ho usato:

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

Ho quindi usato il gem migration_data per aggiungere il tempo per i progetti attuali sulla migrazione come:

def data
  Project.update_all created_at: Time.now
end

Quindi tutti i progetti creati dopo questa migrazione verranno aggiornati correttamente. Assicurati che anche il server venga riavviato in modo che Rails ActiveRecordinizi a tracciare i timestamp sul record.


1

Molte risposte qui, ma posterò anche le mie perché nessuna delle precedenti ha funzionato davvero per me :)

Come alcuni hanno notato, #add_timestampspurtroppo aggiunge la null: falserestrizione, che renderà le vecchie righe invalide perché non hanno questi valori popolati. La maggior parte delle risposte qui suggerisce che abbiamo impostato un valore predefinito ( Time.zone.now), ma non mi piacerebbe farlo perché questi timestamp predefiniti per i vecchi dati non saranno corretti. Non vedo il valore nell'aggiunta di dati errati alla tabella.

Quindi la mia migrazione è stata semplicemente:

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

No null: false, nessun'altra restrizione. Le vecchie righe continueranno a essere valide con created_atas NULLe update_atas NULL(fino a quando non viene eseguito un aggiornamento alla riga). Le nuove righe saranno created_ate updated_atpopolate come previsto.


1

Il problema con la maggior parte delle risposte qui è che se per impostazione predefinita Time.zone.nowtutti i record avranno il tempo in cui la migrazione è stata eseguita come ora predefinita, il che probabilmente non è quello desiderato. In Rails 5 puoi invece usare now(). Ciò imposterà i timestamp per i record esistenti come l'ora di esecuzione della migrazione e l'ora di inizio della transazione di commit per i record appena inseriti.

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


1

L'utilizzo Time.currentè un buon stile 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

o

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

Questo è semplice per aggiungere il timestamp nella tabella esistente.

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

0

Per coloro che non usano Rails ma usano activerecord, quanto segue aggiunge anche una colonna a un modello esistente, l'esempio è per un campo intero.

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

0

E ' change, non è change_tableper Rails 4.2:

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

0

Sembra una soluzione pulita in Rails 5.0.7 (scoperto il metodo 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

Sono su Rails 5.0 e nessuna di queste opzioni ha funzionato.

L'unica cosa che funzionava era usare il tipo per essere: timestamp e non: datetime

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

-1

Ho usato personalmente quanto segue e ho aggiornato tutti i record precedenti con la data / ora corrente:

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

Ho riscontrato lo stesso problema su Rails 5 durante il tentativo di utilizzo

change_table :my_table do |t|
    t.timestamps
end

Sono stato in grado di aggiungere manualmente le colonne timestamp con il seguente:

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

questo non imposterà sempre il valore predefinito con l'ora nel momento in cui è stata eseguita la migrazione? (quindi non proprio un timestamp dinamico gestito dal DB)
— Guillaume Petit

per i record che già esistono nel tuo db, sì, imposteranno create_at e updated_at sul datetime in cui è stata eseguita la migrazione. Senza avere quei valori in anticipo, idk come altrimenti si inizializzerebbe quei valori. EDIT: Sarebbe solo considerato l'inizio della storia di quella fila
— Andres Rosales il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.