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.

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

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

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.


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


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


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

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.