17

Il Rails guides to active record migrations dice che si può faremigrazione reversibile per change_column_default da non avere alcun difetto in Rails

change_column_default :products, :approved, from: true, to: false 

Ho un metodo di change in Rails che è simile al seguente:

change_column_default :people, :height, from: nil, to: 0 

con l'intenzione di passare da non avere valori predefiniti, ad avere un valore predefinito pari a zero.

Tuttavia, quando provo a rotazione indietro, ottengo

ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration 

Considerando do Rotaie un from e to, perché non è di accettare che?

Sto usando Rails 4.2.0.

+0

Potrebbe essere che Rails stia cercando di evitare un problema di coercizione tipo? – max

+0

http://stackoverflow.com/questions/7098602/add-a-default-value-to-a-column-through-a-migration – max

+0

Non vedo alcun parametro 'from' o' to' nella [definizione del metodo] (http://api.rubyonrails.org/classes/ActiveRecord/ConnectionA dapters/PostgreSQL/SchemaStatements.html # method-i-change_column_default) –

risposta

7

La guida legato al la domanda è per il bordo Rails, non per la versione definitiva di Rails.

La sintassi reversibile per change_column_default è il risultato della richiesta di pull 20018. La richiesta pull ha anche aggiornato le guide di Rails per Edge Rails.

Da activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb:

-  def change_column_default(table_name, column_name, default) 
+  # Passing a hash containing +:from+ and +:to+ will make this change 
+  # reversible in migration: 
+  # 
+  # change_column_default(:posts, :state, from: nil, to: "draft") 
+  # 
+  def change_column_default(table_name, column_name, default_or_changes) 

La richiesta di pull è stata fatta 27 Giugno 2015, che è più recente rispetto a qualsiasi versione di Rails rilasciati a partire dal 1 ° agosto 2015.

La documentazione per migration for Rails 4.2.3 riflette il fatto che la sintassi reversibile non è ancora disponibile:

change_column_default :products, :approved, false 
-3

Quando si tenta di eseguire il downgrade del tipo di dati, da value (0) a nil in questo caso, i binari segnaleranno questo errore. Perché potrebbe perdere i dati

Un altro esempio andrebbe da string ->integer.

questo è davvero un good article explain the same

UPDATE

sembra che la migrazione inversa chiama change_column_default in command_recorder(activerecord-4.2.0/lib/active_record/migration/command_recorder.rb#76) si prega di controllare se si imposta il nil come valore di default.

UPDATE2

si scopre che, se si utilizza change nelle migrazioni, quelli devono essere in grado di reversibili

ma change_column non è reversibile attraverso change metodo. Quindi, si dovrà usare il vecchio rotaie metodo di migrazione con up e down

leggere questo articolo spiega il scenario

+1

'0' e' nil' non sono tipi di dati. Inoltre, hai menzionato il concetto generale di migrazione irreversibile, ma non spieghi perché questo cambiamento sarebbe una migrazione irreversibile. –

+0

Dove dovrei impostare 'nil' come valore predefinito? –

+0

@AndrewGrimm, un altro aggiornamento :) – sameera207

0

Prima di tutto, come Jonathan Allard detto il from e to non sono nella fonte metodo che significa che il change_column_default non lo accetta E 'semplicemente così:

def sum(a) 
    return a 
end 

Ora, se si tenta di passare due variabili ad esso come sum(a, b) o qualcosa non accetterà tale diritto. Questo è quello che stai cercando di fare sopra usando from e to.

Ora la sintassi corretta di questo è:

change_column_default(:people, :height, 0) 

Il metodo non accetta from e to (come viene definita come tale, anche se sono le chiavi di hash, se il metodo non fa uso quella coppia di valori chiave ovunque non è utile) e se è una nuova colonna ovviamente avrà il valore predefinito nil (se non impostato prima) e supponiamo che la colonna height di tipo integer e la si assegni di default valore a memorizzerà 0 come valore predefinito (non sicuro al 100% ma ha provato a farlo dalla console di rails). Non importa rotaiare quale sia il valore predefinito al momento, ha solo bisogno del nuovo valore predefinito. Pertanto, se il valore predefinito corrente è 0 e lo si imposta sulle rotaie nil, non verrà presentato reclamo. È il tuo database e desideri che cosa farne. Solo se il database lo interrompe se si sta facendo qualcosa di sbagliato come l'assegnazione di string a boolean, allora ovviamente genererà un errore.

Ora, una volta eseguita questa migrazione, imposterà il valore predefinito su 0, ora le rotaie non conoscono il valore predefinito precedente. Come è andato e non lo ha memorizzato da nessuna parte. Ecco perché change_column_default è una migrazione irreversibile. E se provi a rollarlo, ti viene dato il ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration in caso di metodo change. Significa che quando si è utilizzato:

def change 
    change_column_default(:people, :height, 0) 
end 

Ecco perché per questo tipo di migrazioni usiamo il metodo up e down:

def up 
    change_column_default(:people, :height, 0) 
end 

def down 
    change_column_default(:people, :height, nil) 
end 

Spero che questo aiuti.

+0

'da: nil, a: 0' è uguale a' {: from => nil,: to => 0} ', che è un singolo oggetto. –

+0

In realtà penso che tu non abbia capito il mio punto, il 'change_column_default' accetta solo un singolo valore per l'impostazione del valore predefinito della colonna, ora invece di quello stai scrivendo' from: nil' quindi non ha codice scritto nella sorgente che elaborerà il tuo 'da: nil' e' a: 0'. – Deep

14

se si utilizza MySQL come adattatore, poi in base a questo link http://apidock.com/rails/ActiveRecord/ConnectionAdapters/AbstractMysqlAdapter/change_column_default, la migrazione change_column_default corre come questo

def change_column_default(table_name, column_name, default) #:nodoc: 
column = column_for(table_name, column_name) 
change_column table_name, column_name, column.sql_type, :default => default 
end 

così come lo vedete chiama change_column dentro di sé quando si chiama change_column_default e in base a questo link http://edgeguides.rubyonrails.org/active_record_migrations.html change_column la migrazione è irreversibile.

Questo dimostra perché si ottiene ActiveRecord::IrreversibleMigration: ActiveRecord::IrreversibleMigration

Quindi, se si desidera eseguire la migrazione utilizzando change_column_default si deve aggiungere def up e def down.

Vorrei suggerire di utilizzare change_column come è già stato chiamato all'interno di change_column_default.

def up 
change_column :people, :height, :integer, default: 0 
end 

def down 
change_column :people, :height, :integer, default: nil 
end 
+1

Questa risposta non è perfetta, ma è migliore delle altre risposte (ovviamente, non posso assegnare la taglia a me stesso). –

+0

si @AndrewGrimm hai ragione riguardo la risposta perfetta. ho aggiunto http://edgeguides.rubyonrails.org/ come riferimento che deve ancora essere realizzato. Ancora per aggiungere informazioni, ho controllato questo link http://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html per vedere l'elenco delle migrazioni reversibili. e vedo 'change_column' non è disponibile nella lista. infine grazie per la generosità e grazie anche per la tua risposta. utile..:) – Athar

0

Come di ottobre 2016, questa funzione (usando to: e from: per change_column_default essere reversibili) è ora disponibile sul ramo 5.x. Sfortunatamente non è disponibile 4.2.xo inferiore. :(

verifica:.. git tag --contains f9c841927ac3d1daea2a9cebf08b18e844e5eec5 nel progetto rotaie

0

Giusto per aggiungere un paio di punti se si è bloccato con la versione Rails non sostenere change_column_default reversibili, una possibilità per superare il problema è:

def change 
    # If your dataset is small, just cache in memory, if large, consider file dump here: 
    cache = Table.all 
    # Full column def important for reversibility 
    remove_column :table, :column, :type, { config_hash } 
    # Re-add the column with new default: 
    add_column :table, :column, :type, { config_hash, default: 0 } 
    # Now update the data with cached records (there might be more efficient ways, of course): 
    cache.each do |rec| 
     Table.find(rec.id).update(column: rec.column) 
    end 
end 
Problemi correlati