Jak uniknąć wywołania zwrotnego ActiveRecord?
Mam kilka modeli, które mają wywołania after_save. Zazwyczaj jest to w porządku, ale w niektórych sytuacjach, na przykład podczas tworzenia danych programistycznych, chcę zapisać modele bez uruchamiania wywołań zwrotnych. Czy jest na to prosty sposób? Coś podobnego...
Person#save( :run_callbacks => false )
Lub
Person#save_without_callbacks
Zajrzałem do rails docs i nic nie znalazłem. Jednak z mojego doświadczenia wynika, że rails docs nie zawsze opowiadają całą historię.
UPDATE
Znalazłem wpis na blogu który wyjaśnia jak można usunąć wywołania zwrotne z takiego modelu:
Foo.after_save.clear
Nie mogłem znaleźć, gdzie ta metoda jest udokumentowana, ale wydaje się, że działa.
25 answers
To rozwiązanie jest tylko Rails 2.
Właśnie to zbadałem i myślę, że mam rozwiązanie. Istnieją dwie prywatne metody ActiveRecord, których możesz użyć:update_without_callbacks
create_without_callbacks
Będziesz musiał użyć send, aby wywołać te metody. przykłady:
p = Person.new(:name => 'foo')
p.send(:create_without_callbacks)
p = Person.find(1)
p.send(:update_without_callbacks)
Jest to zdecydowanie coś, co naprawdę chcesz używać tylko w konsoli lub podczas wykonywania losowych testów. Mam nadzieję, że to pomoże!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-03 04:01:55
Użyj update_column
(Rails >= v3.1) lub update_columns
(Rails > = 4.0), aby pominąć wywołania zwrotne i walidacje. Również z tych metod, updated_at
jest nie zaktualizowane.
#Rails >= v3.1 only
@person.update_column(:some_attribute, 'value')
#Rails >= v4.0 only
@person.update_columns(attributes)
Http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update_column
#2: pomijanie wywołań zwrotnych, które działają również podczas tworzenia obiektu
class Person < ActiveRecord::Base
attr_accessor :skip_some_callbacks
before_validation :do_something
after_validation :do_something_else
skip_callback :validation, :before, :do_something, if: :skip_some_callbacks
skip_callback :validation, :after, :do_something_else, if: :skip_some_callbacks
end
person = Person.new(person_params)
person.skip_some_callbacks = true
person.save
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-20 04:22:56
Aktualizacja:
@ Vikrant Chaudhary rozwiązanie wydaje się lepsze:
#Rails >= v3.1 only
@person.update_column(:some_attribute, 'value')
#Rails >= v4.0 only
@person.update_columns(attributes)
Moja oryginalna ODPOWIEDŹ:
Zobacz ten link: Jak pominąć wywołania zwrotne ActiveRecord?
W Kolejach3,
Załóżmy, że mamy definicję klasy:
class User < ActiveRecord::Base
after_save :generate_nick_name
end
Approach1:
User.send(:create_without_callbacks)
User.send(:update_without_callbacks)
Approach2: Jeśli chcesz pominąć je w plikach rspec lub cokolwiek innego, spróbuj tego:
User.skip_callback(:save, :after, :generate_nick_name)
User.create!()
Uwaga: gdy to zrobisz, jeśli nie jesteś w środowisku rspec, powinieneś zresetować wywołania:
User.set_callback(:save, :after, :generate_nick_name)
Działa mi dobrze on rails 3.0.5
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:15
Rails 3:
MyModel.send("_#{symbol}_callbacks") # list
MyModel.reset_callbacks symbol # reset
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-26 08:03:40
Możesz spróbować czegoś takiego w swoim modelu osoby:
after_save :something_cool, :unless => :skip_callbacks
def skip_callbacks
ENV[RAILS_ENV] == 'development' # or something more complicated
end
EDIT: after_save nie jest symbolem, ale to przynajmniej 1000-ty raz próbowałem go zrobić.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-27 14:32:24
Jeśli celem jest po prostu wstawienie rekordu bez wywołania zwrotnego lub walidacji, a chciałbyś to zrobić bez uciekania się do dodatkowych klejnotów, dodawania warunkowych sprawdzeń, używania surowego SQL lub futzing z kodem wychodzącym w jakikolwiek sposób, rozważ użycie "obiektu cienia" wskazującego na istniejącą tabelę db. TAK:
class ImportedPerson < ActiveRecord::Base
self.table_name = 'people'
end
To działa z każdą wersją Rails, jest threadsafe i całkowicie eliminuje wszystkie walidacje i wywołania zwrotne bez modyfikacji istniejącego kodu. Możesz po prostu wrzuć deklarację klasy tuż przed importem i powinieneś być gotowy. Pamiętaj tylko, aby użyć nowej klasy do wstawienia obiektu, na przykład:
ImportedPerson.new( person_attributes )
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-09 22:52:14
Możesz użyć update_columns
:
User.first.update_columns({:name => "sebastian", :age => 25})
Aktualizuje podane atrybuty obiektu, bez wywoływania save, co powoduje pominięcie walidacji i wywołań zwrotnych.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-01 18:51:05
Jedynym sposobem na uniknięcie wszystkich wywołań zwrotnych after_save jest zwrócenie pierwszego false.
Być może mógłbyś spróbować czegoś w stylu (untested):
class MyModel < ActiveRecord::Base
attr_accessor :skip_after_save
def after_save
return false if @skip_after_save
... blah blah ...
end
end
...
m = MyModel.new # ... etc etc
m.skip_after_save = true
m.save
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-21 13:41:53
Wygląda na to, że można to zrobić w Rails 2.3 (ponieważ update_without_callbacks zniknął, itd.), byłoby użycie update_all, która jest jedną z metod, które pomijają wywołania zwrotne zgodnie z Sekcja 12 Przewodnika po walidacjach i wywołaniach zwrotnych.
Zauważ również, że jeśli robisz coś w swoim after_ callback, który wykonuje obliczenia oparte na wielu asocjacjach (np. has_many assoc, gdzie również wykonujesz accepts_nested_attributes_for), będziesz musiał ponownie załadować plik stowarzyszenie, w przypadku gdy jest częścią save, jeden z jego członków został usunięty.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-24 07:39:29
Https://gist.github.com/576546
Po prostu wrzuć tę łatkę do config/initializers / skip_callbacks.rb
Then
Project.skip_callbacks { @project.save }
Lub tym podobne.
Wszystkie podziękowania dla autora
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-24 23:26:42
Najbardziej up-voted
ODPOWIEDŹ może wydawać się myląca w niektórych przypadkach.
Możesz użyć tylko prostego if
sprawdź, czy chcesz pominąć wywołanie zwrotne, jak to:
after_save :set_title, if: -> { !new_record? && self.name_changed? }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-23 09:57:23
Rozwiązaniem, które powinno działać we wszystkich wersjach Rails bez użycia gem lub wtyczki, jest po prostu bezpośrednie wydanie instrukcji aktualizacji. eg
ActiveRecord::Base.connection.execute "update table set foo = bar where id = #{self.id}"
Może to być (lub nie) opcja w zależności od złożoności aktualizacji. Działa to dobrze np. przy aktualizacji FLAG na płycie z wewnątrz wywołanie zwrotne after_save (bez ponownego uruchamiania wywołania zwrotnego).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-08 18:02:40
# for rails 3
if !ActiveRecord::Base.private_method_defined? :update_without_callbacks
def update_without_callbacks
attributes_with_values = arel_attributes_values(false, false, attribute_names)
return false if attributes_with_values.empty?
self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
end
end
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-17 07:21:17
Żaden z tych punktów nie wskazuje na wtyczkę without_callbacks
, która po prostu robi to, czego potrzebujesz ...
class MyModel < ActiveRecord::Base
before_save :do_something_before_save
def after_save
raise RuntimeError, "after_save called"
end
def do_something_before_save
raise RuntimeError, "do_something_before_save called"
end
end
o = MyModel.new
MyModel.without_callbacks(:before_save, :after_save) do
o.save # no exceptions raised
end
Http://github.com/cjbottaro/without_callbacks działa z Rails 2.x
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-01 19:27:15
Napisałem wtyczkę, która implementuje update_without_callbacks w Rails 3:
Http://github.com/dball/skip_activerecord_callbacks
Właściwym rozwiązaniem, myślę, jest przepisanie modeli, aby uniknąć wywołań zwrotnych w pierwszej kolejności, ale jeśli jest to niepraktyczne w najbliższej przyszłości, ta wtyczka może pomóc.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-13 14:59:38
Jeśli używasz Rails 2. Możesz użyć zapytania SQL do aktualizacji kolumny bez uruchamiania wywołań zwrotnych i walidacji.
YourModel.connection.execute("UPDATE your_models SET your_models.column_name=#{value} WHERE your_models.id=#{ym.id}")
Myślę, że powinno działać w każdej wersji rails.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-31 11:31:13
Kiedy potrzebuję pełnej kontroli nad wywołaniem zwrotnym, tworzę inny atrybut, który jest używany jako przełącznik. Proste i skuteczne:
Model:
class MyModel < ActiveRecord::Base
before_save :do_stuff, unless: :skip_do_stuff_callback
attr_accessor :skip_do_stuff_callback
def do_stuff
puts 'do stuff callback'
end
end
Test:
m = MyModel.new()
# Fire callbacks
m.save
# Without firing callbacks
m.skip_do_stuff_callback = true
m.save
# Fire callbacks again
m.skip_do_stuff_callback = false
m.save
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-14 08:39:12
Do tworzenia danych testowych w Rails używasz tego hacka:
record = Something.new(attrs)
ActiveRecord::Persistence.instance_method(:create_record).bind(record).call
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-29 16:15:54
Możesz użyć klejnotu sneaky-save: https://rubygems.org/gems/sneaky-save .
Uwaga to nie może pomóc w zapisywaniu skojarzeń bez walidacji. Wyświetla błąd "created_at nie może być null", ponieważ bezpośrednio wstawia zapytanie sql w przeciwieństwie do modelu. Aby to zaimplementować, musimy zaktualizować wszystkie automatycznie wygenerowane kolumny db.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-14 13:16:17
Potrzebowałem rozwiązania dla Rails 4, więc wpadłem na to:
App / models/concerns / save_without_callbacks.rb
module SaveWithoutCallbacks
def self.included(base)
base.const_set(:WithoutCallbacks,
Class.new(ActiveRecord::Base) do
self.table_name = base.table_name
end
)
end
def save_without_callbacks
new_record? ? create_without_callbacks : update_without_callbacks
end
def create_without_callbacks
plain_model = self.class.const_get(:WithoutCallbacks)
plain_record = plain_model.create(self.attributes)
self.id = plain_record.id
self.created_at = Time.zone.now
self.updated_at = Time.zone.now
@new_record = false
true
end
def update_without_callbacks
update_attributes = attributes.except(self.class.primary_key)
update_attributes['created_at'] = Time.zone.now
update_attributes['updated_at'] = Time.zone.now
update_columns update_attributes
end
end
W dowolnym modelu:
include SaveWithoutCallbacks
Wtedy możesz:
record.save_without_callbacks
Lub
Model::WithoutCallbacks.create(attributes)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-12 15:05:02
Dlaczego chcesz być w stanie to zrobić w rozwoju? Z pewnością będzie to oznaczać, że budujesz swoją aplikację z nieprawidłowymi danymi i jako takie będzie zachowywać się dziwnie, a nie tak, jak oczekujesz w produkcji.
Jeśli chcesz zapełnić swój dev db danymi, lepszym rozwiązaniem byłoby zbudowanie zadania rake, które wykorzystało klejnot fakera do zbudowania poprawnych danych i zaimportowania ich do db, tworząc tyle lub kilka rekordów, ile chcesz, ale jeśli jesteś na nim wygięty i masz dobry powód, myślę, że to update_without_callbacks i create_without_callbacks będą działać dobrze, ale kiedy próbujesz zginać szyny do swojej woli, zadaj sobie pytanie, Czy masz dobry powód i czy to, co robisz, jest naprawdę dobrym pomysłem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-11 09:18:13
Jedną z opcji jest posiadanie oddzielnego modelu dla takich manipulacji, używając tej samej tabeli:
class NoCallbacksModel < ActiveRecord::Base
set_table_name 'table_name_of_model_that_has_callbacks'
include CommonModelMethods # if there are
:
:
end
(to samo podejście może ułatwić omijanie walidacji)
Stephan
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-06 19:00:06
Innym sposobem byłoby użycie haków walidacji zamiast wywołań zwrotnych. Na przykład:
class Person < ActiveRecord::Base
validate_on_create :do_something
def do_something
"something clever goes here"
end
end
W ten sposób można domyślnie uzyskać do_something, ale można go łatwo zastąpić za pomocą:
@person = Person.new
@person.save(false)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-05 14:50:24
Coś, co powinno działać ze wszystkimi wersjami ActiveRecord
bez zależności od opcji lub metod activerecord, które mogą lub nie mogą istnieć.
module PlainModel
def self.included(base)
plainclass = Class.new(ActiveRecord::Base) do
self.table_name = base.table_name
end
base.const_set(:Plain, plainclass)
end
end
# usage
class User < ActiveRecord::Base
include PlainModel
validates_presence_of :email
end
User.create(email: "") # fail due to validation
User::Plain.create(email: "") # success. no validation, no callbacks
user = User::Plain.find(1)
user.email = ""
user.save
TLDR: użyj "innego modelu activerecord" w tej samej tabeli
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-07 05:17:11
Nie jest to najczystszy sposób, ale możesz zawinąć kod wywołania zwrotnego w taki sposób, aby sprawdzał środowisko Rails.
if Rails.env == 'production'
...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-10 23:59:26