Wie ändere ich eine nullfähige Spalte in einer Rails-Migration in nicht nullbar?

188

Ich habe in einer früheren Migration eine Datumsspalte erstellt und diese auf null gesetzt. Jetzt möchte ich es so ändern, dass es nicht nullbar ist. Wie gehe ich vor, wenn diese Datenbank Nullzeilen enthält? Ich kann diese Spalten auf Time.now setzen, wenn sie derzeit null sind.

Kevin Pang
quelle

Antworten:

204

Wenn Sie es in einer Migration tun, könnten Sie es wahrscheinlich so machen:

# 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
DanneManne
quelle
1
Nur eine Anmerkung, weil ich dadurch meine Entwicklerdatenbank kaputt gemacht habe. Verwenden Sie lieber eine explizite Hash-Syntax wie folgt : MyModel.update_all({:date_column => Time.now}, {:date_column => nil}). Die Abfrage in Ihrem ursprünglichen Formular hat gerade dazu geführt, dass alle meine Modelle im Feld keinen Wert haben.
dimitarvp
Danke für das Update. Ich weiß, dass dies nicht der Fall war, als ich diese Antwort schrieb, aber ich kann mich nicht erinnern, welche Version von Ruby oder RoR ich zu der Zeit verwendet habe.
DanneManne
1
Haben Sie die Möglichkeit, bei dieser Migration die Methode 'up' / 'down' zu verwenden, oder können Sie die einfache Änderungsmethode bei der Migration verwenden?
EE33
2
Die changeMethode ist für diesen Fall nicht so geeignet, da (1) die update_allMethode sowohl bei der Migration als auch bei einer möglichen Wiederherstellung ausgeführt wird. Das ist vielleicht nicht das Schlimmste, aber weil (2) die Migration keine Möglichkeit hat zu wissen, gegenüber was die Spalte bei einem möglichen Zurücksetzen geändert wurde. Also für diesen Fall würde ich bei upund bleiben down.
DanneManne
2
Für alle Interessierten zeigt meine Antwort , wie das in einem einzigen Schritt geht.
Rick Smith
167

In Rails 4 ist dies eine bessere (DRYer) Lösung:

change_column_null :my_models, :date_column, false

Um sicherzustellen, dass keine Datensätze mit NULLWerten in dieser Spalte vorhanden sind, können Sie einen vierten Parameter übergeben. Dies ist der Standardwert, der für Datensätze mit NULLWerten verwendet wird:

change_column_null :my_models, :date_column, false, Time.now
mrbrdo
quelle
4
Dies führt zu Problemen, wenn die Tabelle bereits Nullwerte enthält. Siehe meine Antwort
Rick Smith
5
Auch in 3.2 verfügbar. Hat auch einen 4. Parameter zum Festlegen des Standards, bei dem der Wert null ist.
Toxaq
1
Plus 1 für change_column_null. Der obige Kommentar von Rick Smith weist jedoch auf einen sehr gültigen Fall hin.
0112
Aktualisiert, um die Abfrage zum Aktualisieren von Nullwerten hinzuzufügen. Der 4. Parameter (Standardwert) ist nur nützlich, wenn Sie auch für zukünftige Datensätze einen Standardwert haben möchten.
Mrbrdo
3
Gemäß den Rails 4.2-Dokumenten legt der 4. Parameter KEINEN Standardwert für zukünftige Datensätze fest: "Die Methode akzeptiert ein optionales viertes Argument, um vorhandene + NULL + s durch einen anderen Wert zu ersetzen. Bitte beachten Sie, dass das vierte Argument nicht festgelegt wird die Standardeinstellung einer Spalte. "
Mike Fischer
70

Schienen 4 (andere Antworten auf Schienen 4 haben Probleme):

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

Das Ändern einer Spalte mit NULL-Werten, um NULL nicht zuzulassen, führt zu Problemen. Dies ist genau die Art von Code, die in Ihrem Entwicklungssetup einwandfrei funktioniert und dann abstürzt, wenn Sie versuchen, ihn in Ihrer LIVE- Produktion bereitzustellen . Sie sollten zuerst NULL-Werte in etwas Gültiges ändern und dann NULL-Werte nicht zulassen. Der 4. Wert in change_column_nullmacht genau das. Weitere Informationen finden Sie in der Dokumentation .

Außerdem ziehe ich es im Allgemeinen vor, einen Standardwert für das Feld festzulegen, damit ich den Wert des Felds nicht jedes Mal angeben muss, wenn ich ein neues Objekt erstelle. Dazu habe ich auch den auskommentierten Code eingefügt.

Rick Smith
quelle
3
Für Rails 4 scheint dies die genaueste und vollständigste Antwort zu sein, einschließlich der auskommentierten Standardeinstellung.
Mike Fischer
4
Wenn Sie einer Tabelle eine neue Spalte hinzufügen und neue Werte für null einfügen möchten, aber keinen Standardwert für die Spalte hinzufügen möchten, können Sie dies in Ihrer Migration tun: add_column :users, :admin, :stringthenchange_column_null(:admin, :string, false, "new_value_for_existing_records")
colsen
34

Erstellen Sie eine Migration mit einer change_columnAnweisung mit einem :default =>Wert.

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

Siehe: change_column

Abhängig vom Datenbankmodul müssen Sie möglicherweise verwenden change_column_null

Jessecurry
quelle
1
Das hat bei mir funktioniert. Lokale Verwendung von MySQL. Wenn die App in Heroku (Postgres) gepusht und ausgeführt wurde, hat sie auf eine Spalte gekackt, die nicht null war, als ich sie zu Recht als null schrieb. Nur "change_column_null" würde funktionieren, könnte "change_column ...: null => false" in MySql nicht verwenden. Vielen Dank.
RTFMINC
1
Also, was war Ihre Migration nach change_column_null
js111
1
Postges ist strenger als MySQL - ich würde erwarten, dass es erforderlich wäre change_column_null.
Jessecurry
3
@rtfminc Ich empfehle Ihnen dringend, in der Entwicklung und in der Produktion dasselbe Datenbankmodul zu verwenden, da dadurch viele Probleme in Bezug auf Randfälle vermieden werden.
Yagooar
12

Schienen 4:

def change
  change_column_null(:users, :admin, false )
end
Piratenbroadcast
quelle
1
Bitte geben Sie eine Beschreibung Ihrer Antworten.
Wahyu Kristianto
3

In Rails 4.02+ gibt es laut Dokumentation keine Methode wie update_allbei 2 Argumenten. Stattdessen kann man diesen Code verwenden:

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

Sie können add_timestamps und null: false nicht verwenden, wenn Sie über vorhandene Datensätze verfügen. Hier ist die Lösung:

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
Nicolas Maloeuvre
quelle