Jak zmienić kolumnę zerowalną na nie dopuszczalną zerową w migracji Railsów?

188

W poprzedniej migracji utworzyłem kolumnę daty i ustawiłem ją na wartość null. Teraz chcę to zmienić, aby nie było zerowalne. Jak mam to zrobić, zakładając, że w tej bazie danych są puste wiersze? Nie przeszkadza mi ustawienie tych kolumn na Time.now, jeśli obecnie są one puste.

Kevin Pang
źródło

Odpowiedzi:

204

Jeśli wykonasz to podczas migracji, prawdopodobnie możesz to zrobić w następujący sposób:

# 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
źródło
1
Tylko uwaga, bo to spowodowało, że zepsułem bazę danych deweloperów. Raczej używać wyraźny składni skrótu, tak: MyModel.update_all({:date_column => Time.now}, {:date_column => nil}). Zapytanie w oryginalnej formie właśnie sprawiło, że wszystkie moje modele mają zero wartości w polu.
dimitarvp
Dziękuję za aktualizację. Wiem, że nie było tak, kiedy napisałem tę odpowiedź, ale nie pamiętam, której wersji Ruby lub RoR używałem w tym czasie.
DanneManne,
1
Czy w tej migracji używasz metody „w górę” / „w dół”, czy możesz prostą metodę zmiany w migracji?
EE33,
2
changeMetoda nie jest tak nadaje się do tej sprawy, ponieważ (1) update_allmetoda będzie realizowane zarówno na migrować i potencjalnego Przywróć. To może nie być najgorsze, ale ponieważ (2) migracja nie ma możliwości dowiedzenia się, z czego zmieniono kolumnę w potencjalnym przywróceniu. Więc w tym przypadku trzymałbym się upi down.
DanneManne
2
Dla wszystkich zainteresowanych moja odpowiedź pokazuje, jak to zrobić w jednym kroku.
Rick Smith
167

W Rails 4 jest to lepsze (DRYer) rozwiązanie:

change_column_null :my_models, :date_column, false

Aby upewnić się, że nie ma żadnych rekordów z NULLwartościami w tej kolumnie, możesz przekazać czwarty parametr, który jest wartością domyślną używaną dla rekordów z NULLwartościami:

change_column_null :my_models, :date_column, false, Time.now
mrbrdo
źródło
4
Powoduje to problemy, gdy tabela ma już wartości null. Zobacz moją odpowiedź
Rick Smith
5
Dostępne również w wersji 3.2. Ma również czwarty parametr do ustawiania wartości domyślnej, gdzie wartość jest równa null.
toxaq 10.10.14
1
Plus 1 za change_column_null. Jednak powyższy komentarz Ricka Smitha wskazuje na bardzo ważny przypadek.
0112,
Zaktualizowano, aby dodać zapytanie o aktualizację wartości pustych. Czwarty parametr (wartość domyślna) jest przydatny tylko wtedy, gdy faktycznie chcesz mieć wartość domyślną również dla przyszłych rekordów.
mrbrdo
3
W rzeczywistości, zgodnie z dokumentacją Rails 4.2, czwarty parametr NIE ustawia domyślnej wartości dla przyszłych rekordów: „Metoda przyjmuje opcjonalny czwarty argument, aby zastąpić istniejące + NULL + s inną wartością. Uwaga: czwarty argument nie ustawia kolumna jest domyślna ”.
Mike Fischer,
70

Railsy 4 (inne odpowiedzi na Railsy 4 mają problemy):

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

Zmiana kolumny z wartościami NULL, aby nie zezwalać na NULL, spowoduje problemy. Jest to dokładnie ten rodzaj kodu, który będzie działał dobrze w konfiguracji programistycznej, a następnie zawiesi się, gdy spróbujesz wdrożyć go do produkcji LIVE . Najpierw powinieneś zmienić wartości NULL na coś prawidłowego, a następnie zabronić wartości NULL. Czwarta wartość w change_column_nulldokładnie to robi. Więcej informacji znajduje się w dokumentacji .

Ponadto ogólnie wolę ustawić wartość domyślną dla pola, więc nie będę musiał określać wartości pola za każdym razem, gdy tworzę nowy obiekt. Dołączyłem również skomentowany kod, aby to zrobić.

Rick Smith
źródło
3
W przypadku Rails 4 wydaje się to najdokładniejsza i pełna odpowiedź, w tym skomentowane ustawienie domyślne.
Mike Fischer,
4
Jeśli dodajesz nową kolumnę do tabeli i chcesz wstawić nowe wartości null, ale nie chcesz dodawać wartości domyślnej dla kolumny, możesz to zrobić podczas migracji: add_column :users, :admin, :stringa następniechange_column_null(:admin, :string, false, "new_value_for_existing_records")
colsen
34

Utwórz migrację, która ma change_columninstrukcję z :default =>wartością.

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

Zobacz: change_column

W zależności od silnika bazy danych może być konieczne użycie change_column_null

jessecurry
źródło
1
To zadziałało dla mnie. Lokalne używanie MySql. Kiedy pchnąłem i uruchomiłem aplikację w Heroku (Postgres), załamała się kolumna, która nie była zerowa, gdy pisałem ją jako zerową - słusznie. Tylko „change_column_null” działałoby, nie można użyć „change_column ...: null => false” na MySql. Dzięki.
rtfminc
1
więc jaka była Twoja migracja po change_column_null
js111
1
Postges jest bardziej rygorystyczny niż MySQL - oczekiwałbym, że będzie wymagać change_column_null.
jessecurry
3
@rtfminc Zdecydowanie zalecam korzystanie z tego samego silnika bazy danych w fazie projektowania i produkcji, ponieważ pozwala to uniknąć wielu problemów związanych z przypadkowymi przypadkami.
yagooar
12

Szyny 4:

def change
  change_column_null(:users, :admin, false )
end
piratebroadcast
źródło
1
Podaj opis swoich odpowiedzi.
Wahyu Kristianto
3

W Railsach 4.02+ zgodnie z dokumentacją nie ma metody podobnej update_alldo 2 argumentów. Zamiast tego można użyć tego kodu:

# 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
źródło
2

Nie możesz używać add_timestamps i null: false, jeśli masz istniejące rekordy, więc oto rozwiązanie:

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
źródło