Come modificare una colonna nullable in non nullable in una migrazione di Rails?


188

Ho creato una colonna data in una precedente migrazione e l'ho impostata come nullable. Ora voglio cambiarlo per non essere nullable. Come faccio a fare questo supponendo che ci siano righe null in quel database? Sono d'accordo con l'impostazione di quelle colonne su Time.now se sono attualmente null.

Risposte:


204

Se lo fai in una migrazione, probabilmente potresti farlo in questo modo:

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false

1
Solo una nota, perché questo mi ha fatto fallire il mio database di sviluppo. Piuttosto utilizzare la sintassi hash esplicito, in questo modo: MyModel.update_all({:date_column => Time.now}, {:date_column => nil}). La query nella tua forma originale ha fatto sì che tutti i miei modelli abbiano un valore nullo nel campo.
dimitarvp

Grazie per l'aggiornamento. So che non è stato così quando ho scritto questa risposta, ma non ricordo quale versione di Ruby o RoR stavo usando in quel momento.
DanneManne,

1
Hai utilizzato il metodo "su" / "giù" in questa migrazione o puoi utilizzare il metodo di modifica semplice nella migrazione?
EE33,

2
Il changemetodo non è così adatto in questo caso perché (1) il update_allmetodo verrà eseguito sia sulla migrazione che su un potenziale ripristino. Potrebbe non essere la cosa peggiore, ma perché (2) la migrazione non ha modo di sapere da cosa è stata modificata la colonna in un potenziale ripristino. Quindi per questo caso rimarrei con upe down.
DanneManne,

2
Per chiunque sia interessato, la mia risposta mostra come farlo in un solo passaggio.
Rick Smith,

167

In Rails 4, questa è una soluzione migliore (DRYer):

change_column_null :my_models, :date_column, false

Per assicurarsi che non esistano record con NULLvalori in quella colonna, è possibile passare un quarto parametro, che è il valore predefinito da utilizzare per i record con NULLvalori:

change_column_null :my_models, :date_column, false, Time.now

4
Ciò causa problemi quando la tabella ha già valori null. Vedi la mia risposta
Rick Smith

5
Disponibile anche in 3.2. Ha anche un quarto parametro per impostare il valore predefinito dove i valori sono null.
Toxaq,

1
Più 1 per change_column_null. Tuttavia, il commento di Rick Smith sopra sottolinea un caso molto valido.
0112

Aggiornato per aggiungere la query per l'aggiornamento di valori null. Il 4o parametro (valore predefinito) è utile solo quando si desidera avere un valore predefinito anche per i record futuri.
mrbrdo,

3
In realtà, secondo i documenti di Rails 4.2, il quarto parametro NON imposta un valore predefinito per i record futuri: "Il metodo accetta un quarto argomento facoltativo per sostituire i + NULL + esistenti con qualche altro valore. Si noti che il quarto argomento non imposta impostazione predefinita di una colonna ".
Mike Fischer,

70

Rails 4 (altre risposte di Rails 4 hanno problemi):

def change
  change_column_null(:users, :admin, false, <put a default value here> )
  # change_column(:users, :admin, :string, :default => "")
end

La modifica di una colonna con valori NULL al suo interno per non consentire NULL causerà problemi. Questo è esattamente il tipo di codice che funzionerà bene nella configurazione di sviluppo e poi si arresta in modo anomalo quando si tenta di distribuirlo nella produzione LIVE . È necessario innanzitutto modificare i valori NULL in qualcosa di valido e quindi non consentire NULL. Il 4 ° valore in change_column_nullfa esattamente questo. Vedi la documentazione per maggiori dettagli.

Inoltre, generalmente preferisco impostare un valore predefinito per il campo, quindi non dovrò specificare il valore del campo ogni volta che creo un nuovo oggetto. Ho incluso anche il codice commentato per farlo.


3
Per Rails 4, questa sembra essere la risposta più accurata e completa, inclusa l'impostazione predefinita commentata.
Mike Fischer,

4
Se stai aggiungendo una nuova colonna a una tabella e vuoi inserire nuovi valori per null, ma non vuoi aggiungere un valore predefinito per la colonna, puoi farlo nella tua migrazione: add_column :users, :admin, :stringquindichange_column_null(:admin, :string, false, "new_value_for_existing_records")
colsen

34

Creare una migrazione change_columncon un'istruzione con un :default =>valore.

change_column :my_table, :my_column, :integer, :default => 0, :null => false

Vedi: change_column

A seconda del motore di database potrebbe essere necessario utilizzare change_column_null


1
Questo ha funzionato per me. Utilizzando MySql localmente. Quando è stato premuto ed eseguito l'app in Heroku (Postgres), è saltato su una colonna che non era nulla quando lo stavo scrivendo, giustamente. Solo "change_column_null" funzionerebbe, non è possibile utilizzare "change_column ...: null => false" su MySql. Grazie.
RTFMINC

1
quindi qual è stata la tua migrazione dopo change_column_null
js111 il

1
Postges è più rigoroso di MySQL: mi aspetto che lo richieda change_column_null.
jessecurry,

3
@rtfminc Ti consiglio vivamente di utilizzare lo stesso motore di database nello sviluppo e nella produzione, in quanto evita molti problemi quando si tratta di casi limite.
yagooar


3

In Rails 4.02+ secondo i documenti non esiste un metodo simile update_alla 2 argomenti. Invece si può usare questo codice:

# Make sure no null value exist
MyModel.where(date_column: nil).update_all(date_column: Time.now)

# Change the column to not allow null
change_column :my_models, :date_column, :datetime, null: false

2

Non puoi utilizzare add_timestamps e null: false se hai record esistenti, quindi ecco la soluzione:

def change
  add_timestamps(:buttons, null: true)

  Button.find_each { |b| b.update(created_at: Time.zone.now, updated_at: Time.zone.now) }

  change_column_null(:buttons, :created_at, false)
  change_column_null(:buttons, :updated_at, false)
end
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.