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.