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.

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

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

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ć.


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


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


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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.