2016-04-18 18 views
5

Mam przypadek użycia, w którym chciałbym użyć metody ActiveRecord :: Relation update_all i określić kilka pól do ustawienia. Używam update_all, ponieważ wiele wpisów może być aktualizowanych i nie chcę ładować ich wszystkich i aktualizować je jeden po drugim.Rails ActiveRecord - update_all: jak aktualizować wiele pól naraz z mieszanym typem argumentów

Niektóre z nich wymagają bezpośredniej instrukcji SQL SET, na przykład dlatego, że ustawiam kolumnę zgodnie z wartością innej kolumny.

Czy istnieje prosta składnia z update_all aby ten czytelny, wzdłuż linii niniejszego =>

MyModel.where(state: :foo).update_all([ 
    'timespent = timespent + 500', # Can't be anything else than a SQL statement 
    state: :bar, # Regular Rails SQL interpolation 
    updated_at: DateTime.current 
]) 

Zauważ, że składnia powyżej nie działa, ponieważ będzie próbować szukać zastępczych i zastąpić wartości , dlatego state: :bar i updated_at: DateTime.current są ignorowane.

Częściowym rozwiązaniem tego problemu byłoby przekonwertowanie wszystkiego na pojedynczy ciąg instrukcji SQL, jak na przykład poniżej, ale nie podoba mi się to zbytnio, ponieważ muszę wymyślić instrukcje SQL i jest to nieco zbyt skomplikowane podczas kodowania z Szyny;):

MyModel.where(state: :foo).update_all([ 
    "timespent = timespent + 500", # Can't be anything else than a SQL statement 
    "state = 'bar'", 
    "updated_at = NOW()" # Needed to translate from Rails to PGSQL 
].join(', ')) 

Ktoś z eleganckim rozwiązaniem tam?

Dzięki!

+0

Jeśli chcesz mieć silną aplikację i zoptymalizować, będziesz musiał wykonać kwerendy SQL ręcznie kilka razy. Lepiej uczyć się i doskonalić swoje umiejętności SQL, wykonując zwykłą aktualizację SQL niż próbując używać Railsów do wykonywania skomplikowanych zapytań SQL. – MrYoshiji

Odpowiedz

5

update_all pobiera łańcuch, tablicę lub krzyżyk (ale nie można mieszać i dopasowywać). W przypadku tablicy oczekuje warunków ActiveRecord Array. Tak więc powinieneś być w stanie zrobić coś takiego:

MyModel.where(state: :foo).update_all([ 
    'timespent = timespent + 500, state = ?, updated_at: ?', 
    'bar', 
    DateTime.current 
]) 
+1

Uwaga, powinno być 'updated_at ='. – tadman