Szyny update_attributes bez zapisywania?

386

Czy istnieje alternatywa dla update_attributes, która nie zapisuje rekordu?

Więc mógłbym zrobić coś takiego:

@car = Car.new(:make => 'GMC')
#other processing
@car.update_attributes(:model => 'Sierra', :year => "2012", :looks => "Super Sexy, wanna make love to it")
#other processing
@car.save

BTW, wiem, że mogę @car.model = 'Sierra', ale chcę je wszystkie zaktualizować w jednym wierszu.

tybro0103
źródło
co masz na myśli mówiąc „nie zapisuj rekordu”?
Anatolij
update_attributes zapisuje model DB. Zastanawiam się, czy istnieje podobna metoda, która tego nie robi.
tybro0103
3
przypisuje metodę nieniszczącą. Zobacz API w celu uzyskania szczegółowych informacji
Anatolij
3
Możesz użyć update_column (nazwa, wartość) Aktualizuje pojedynczy atrybut obiektu, bez wywoływania komendy save. 1. Walidacja jest pomijana. 2. Połączenia zwrotne są pomijane. 3. kolumna updated_at / updated_on nie jest aktualizowana, jeśli ta kolumna jest dostępna. apidock.com/rails/ActiveRecord/Persistence/update_column
Antoine
10
W wersji 3.1+ użyj assign_attributes apidock.com/rails/ActiveRecord/Base/assign_attributes
elado,

Odpowiedzi:

596

Wierzę, że to, czego szukasz assign_attributes.

Jest to w zasadzie to samo, co update_attributes, ale nie zapisuje rekordu:

class User < ActiveRecord::Base
  attr_accessible :name
  attr_accessible :name, :is_admin, :as => :admin
end

user = User.new
user.assign_attributes({ :name => 'Josh', :is_admin => true }) # Raises an ActiveModel::MassAssignmentSecurity::Error
user.assign_attributes({ :name => 'Bob'})
user.name        # => "Bob"
user.is_admin?   # => false
user.new_record? # => true
Ajedi32
źródło
Twój przykład jest trochę mylący, ponieważ nie wkleiłeś tej linii z modelu attr_accessible :is_admin, :as => :admin:;)
Robin
@Robin Lub po prostu: attr_protected :is_admin. Lub: attr_accessible :nameChodzi o to, że w tym przykładzie: is_admin jest chroniony. Powinienem również zauważyć, że próba masowego przypisania chronionego atrybutu .assign_attributesrzeczywiście podnosi wartość ActiveModel::MassAssignmentSecurity::Error, nawet jeśli nie pokazano tego w przykładzie.
Ajedi32
Tak, ale moja linia pochodzi z dokumentu, z którym się łączysz. Mówię tylko, że powinieneś skopiować / wkleić cały przykład. Ale tak, możesz po prostu powiedzieć, że jest chroniony.
Robin,
@Robin Zaktualizuję przykład, aby był bardziej szczegółowy. Przykład w dokumentacji jest również nieco mylący, ponieważ nie wspomina, że user.assign_attributes({ :name => 'Josh', :is_admin => true })wywołuje komunikat o błędzie i nie ustawia właściwości nazwy użytkownika.
Ajedi32
7
Opcja assign_attributes jest dostępna od wersji Rails 3.1, więc nie możesz jej użyć, jeśli nadal używasz starej wersji Rails.
Haegin,
173

Możesz użyć assign_attributeslub attributes=(są takie same)

Zaktualizuj ściągawki metod (dla Rails 6):

  • update= assign_attributes+save
  • attributes= = alias assign_attributes
  • update_attributes = przestarzałe, alias update

Źródło:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/persistence.rb
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_assignment .rb

Kolejna ściągawka:
http://www.davidverhasselt.com/set-attributes-in-activerecord/#cheat-sheet

Yarin
źródło
1
Jasne i krótkie. Dzięki.
freemanoid
1
w przypadku .attributes = val, jeśli twój model ma_jedno i akceptuje_nested_atrybuty_ dla innego modelu, przekazanie tego_modelu_atrybuty (bez identyfikatora) spowoduje usunięcie istniejącego modelu has_one, nawet jeśli nie trwałeś (np. zapisz). Ale przypisanie atrybutów nie zachowuje się w ten sposób.
ClassyPimp
65

Możesz użyć metody „atrybuty”:

@car.attributes = {:model => 'Sierra', :years => '1990', :looks => 'Sexy'}

Źródło: http://api.rubyonrails.org/classes/ActiveRecord/Base.html

atrybuty = (new_attributes, guard_protected_attributes = true) Pozwala ustawić wszystkie atrybuty na raz, przekazując skrót z kluczami pasującymi do nazw atrybutów (które ponownie pasują do nazw kolumn).

Jeśli wartość guard_protected_attributes ma wartość true (wartość domyślna), wówczas wrażliwe atrybuty można zabezpieczyć przed tą formą przypisywania masy za pomocą makra attr_protected. Lub możesz alternatywnie określić, do których atrybutów można uzyskać dostęp za pomocą makra attr_accessible. Wówczas wszystkie atrybuty, które nie są w nim uwzględnione, nie będą mogły być przypisywane masowo.

class User < ActiveRecord::Base
  attr_protected :is_admin
end

user = User.new
user.attributes = { :username => 'Phusion', :is_admin => true }
user.username   # => "Phusion"
user.is_admin?  # => false

user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
user.is_admin?  # => true
bruno077
źródło
7

Do masowego przypisywania wartości do modelu ActiveRecord bez zapisywania należy użyć metody assign_attributeslub attributes=. Te metody są dostępne w Rails 3 i nowszych. Istnieją jednak niewielkie różnice i problemy związane z wersją, o których należy pamiętać.

Obie metody wykorzystują to użycie:

@user.assign_attributes{ model: "Sierra", year: "2012", looks: "Sexy" }

@user.attributes = { model: "Sierra", year: "2012", looks: "Sexy" }

Zauważ, że żadna z metod nie przeprowadzi walidacji ani nie wykona wywołań zwrotnych; oddzwanianie i sprawdzanie poprawności nastąpi, gdysave wywołaniu.

Szyny 3

attributes=różni się nieznacznie od tego assign_attributesw Rails 3. attributes=sprawdzi, czy przekazany mu argument to Hash, i natychmiast zwróci, jeśli nie jest; assign_attributesnie ma takiego testu mieszania. Zobacz dokumentację interfejsu API przypisywania atrybutów ActiveRecord dlaattributes= .

Poniższy nieprawidłowy kod po cichu zakończy się niepowodzeniem, po prostu powróci bez ustawiania atrybutów:

@user.attributes = [ { model: "Sierra" }, { year: "2012" }, { looks: "Sexy" } ]

attributes= będą po cichu zachowywać się tak, jakby zadania zostały wykonane pomyślnie, a tak naprawdę nie były.

Ten nieprawidłowy kod zgłosi wyjątek, gdy assign_attributesspróbuje skreślić klucze skrótu w otaczającej tablicy:

@user.assign_attributes([ { model: "Sierra" }, { year: "2012" }, { looks: "Sexy" } ])

assign_attributeszgłosi NoMethodErrorwyjątek dla stringify_keys, wskazując, że pierwszy argument nie jest skrótem. Sam wyjątek nie jest bardzo pouczający o rzeczywistej przyczynie, ale fakt, że występuje wyjątek, jest bardzo istotny ważny.

Jedyną różnicą między tymi przypadkami jest metoda zastosowana do przypisania masy: attributes=po cichu udaje się i assign_attributespodnosi wyjątek informujący o wystąpieniu błędu.

Te przykłady mogą wydawać się wymyślone i są do pewnego stopnia, ale ten typ błędu może łatwo wystąpić podczas konwersji danych z interfejsu API lub nawet po prostu przy użyciu serii transformacji danych i zapominania o Hash[]wynikach końcowych .map. Utrzymaj 50 linii kodu powyżej i 3 funkcje usunięte z przypisania atrybutu, a masz przepis na niepowodzenie.

Lekcja z Rails 3 jest taka: zawsze używaj assign_attributeszamiast attributes=.

Szyny 4

W Rails 4 attributes=jest po prostu aliasem do assign_attributes. Zobacz dokumentację interfejsu API przypisywania atrybutów ActiveRecord dlaattributes= .

W przypadku Rails 4 każdą metodę można stosować zamiennie. Niepowodzenie przekazania skrótu jako pierwszego argumentu spowoduje bardzo pomocny wyjątek:ArgumentError: When assigning attributes, you must pass a hash as an argument.

Walidacje

Jeśli przygotowujesz się do lotu przed przygotowaniem do save, możesz również chcieć sprawdzić poprawność przed zapisaniem. Możesz użyć do tego metod valid?i invalid?. Obie zwracają wartości logiczne. valid?zwraca true, jeśli niezapisany model przejdzie wszystkie walidacje, lub false, jeśli nie. invalid?jest po prostu odwrotnościąvalid?

valid? można użyć w następujący sposób:

@user.assign_attributes{ model: "Sierra", year: "2012", looks: "Sexy" }.valid?

Umożliwi to obsługę wszelkich problemów związanych z weryfikacją przed rozmową telefoniczną save.

Michael Gaskill
źródło