Migracje Railsów: Sprawdź istnienie i kontynuuj pracę?

80

Robiłem takie rzeczy podczas moich migracji:

add_column :statuses, :hold_reason, :string rescue puts "column already added"

ale okazuje się, że chociaż działa to dla SQLite, nie działa dla PostgreSQL . Wydaje się, że jeśli add_column wybuchnie, nawet jeśli wyjątek zostanie przechwycony, transakcja jest martwa i migracja nie może wykonać żadnej dodatkowej pracy.

Czy są jakieś specyficzne sposoby sprawdzenia, czy kolumna lub tabela już istnieje? Jeśli to się nie uda, czy jest jakiś sposób, aby mój blok ratunkowy naprawdę działał?

Dan Rosenstark
źródło
Należy wspomnieć, że migracja warunkowa prowadzi do problemów z wycofywaniem, ponieważ na etapie wycofywania nie wiadomo, jakie były warunki podczas migracji do przodu
oklas 13.02.2020
Wykonaj tylko nieobowiązkową część w wycofywaniu
Dan Rosenstark

Odpowiedzi:

175

Począwszy od Rails 3.0 i późniejszych, możesz użyć column_exists?do sprawdzenia istnienia kolumny.

unless column_exists? :statuses, :hold_reason
  add_column :statuses, :hold_reason, :string
end

Jest też table_exists?funkcja, która sięga wstecz do Railsów 2.1.

Tobias Cohen
źródło
Czy za najlepszą praktykę uważa się sprawdzenie, czy kolumna / tabela istnieje przed jej dodaniem / utworzeniem? (Wiem oczywiście, że to zależy od problemu w rękach)
Aldo 'xoen' Giambelluca
4
Czy to działa z wycofywaniem, jeśli zdefiniuję to w metodzie zmiany?
dardub
1
Tak, wycofanie byłoby problemem ... nie jesteśmy pewni, czy powinniśmy usunąć kolumnę, czy nie ... ponieważ nie rejestrujemy poprzedniego stanu.
songyy
8

Albo jeszcze krócej

add_column :statuses, :hold_reason, :string unless column_exists? :statuses, :hold_reason
SG 86
źródło
byłby to komentarz do drugiej odpowiedzi, a nie odpowiedź. Dzięki.
Dan Rosenstark
4

W przypadku Rails 2.X możesz sprawdzić istnienie kolumn, wykonując następujące czynności:

columns("[table-name]").index {|col| col.name == "[column-name]"}

Jeśli zwraca nil, taka kolumna nie istnieje. Jeśli zwraca Fixnum, kolumna istnieje. Oczywiście możesz umieścić bardziej selektywne parametry między {...}kolumnami, jeśli chcesz identyfikować kolumnę nie tylko na podstawie jej nazwy, na przykład:

{ |col| col.name == "foo" and col.sql_type == "tinyint(1)" and col.primary == nil }

(ta odpowiedź została po raz pierwszy opublikowana na Jak pisać migracje warunkowe w railsach? )

JellicleCat
źródło
0

add_column :statuses, :hold_reason, :string unless Status.column_names.include?("hold_reason")

Denis Neverov
źródło