Как я могу избежать запуска обратных вызовов ActiveRecord? - PullRequest
130 голосов
/ 11 марта 2009

У меня есть некоторые модели с обратными вызовами after_save. Обычно это нормально, но в некоторых ситуациях, например, при создании данных разработки, я хочу сохранить модели без запуска обратных вызовов. Есть ли простой способ сделать это? Что-то похожее на ...

Person#save( :run_callbacks => false )

или

Person#save_without_callbacks

Я посмотрел в документации по Rails и ничего не нашел. Однако, по моему опыту, документы Rails не всегда рассказывают всю историю.

UPDATE

Я нашел пост в блоге , в котором объясняется, как можно удалить обратные вызовы из модели, подобной этой:

Foo.after_save.clear

Я не смог найти, где описан этот метод, но, похоже, он работает.

Ответы [ 25 ]

217 голосов
/ 12 сентября 2011

Используйте update_column (Rails> = v3.1) или update_columns (Rails> = 4.0) для пропуска обратных вызовов и проверок. Также с этими методами updated_at обновляется , а не .

#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: Пропуск обратных вызовов, которые также работают при создании объекта

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
72 голосов
/ 11 марта 2009

Это решение только для Rails 2.

Я только что исследовал это, и я думаю, что у меня есть решение. Существует два частных метода ActiveRecord, которые вы можете использовать:

update_without_callbacks
create_without_callbacks

Вам придется использовать send для вызова этих методов. Примеры:

p = Person.new(:name => 'foo')
p.send(:create_without_callbacks)

p = Person.find(1)
p.send(:update_without_callbacks)

Это определенно то, что вы действительно захотите использовать только в консоли или во время случайных тестов. Надеюсь, это поможет!

28 голосов
/ 30 ноября 2011

Обновлено:

@ Решение Викранта Чаудхари выглядит лучше:

#Rails >= v3.1 only
@person.update_column(:some_attribute, 'value')
#Rails >= v4.0 only
@person.update_columns(attributes)

Мой оригинальный ответ:

см. Эту ссылку: Как пропустить обратные вызовы ActiveRecord?

в Rails3,

предположим, у нас есть определение класса:

class User < ActiveRecord::Base
  after_save :generate_nick_name
end 

Подход 1:

User.send(:create_without_callbacks)
User.send(:update_without_callbacks)

Approach2: Если вы хотите пропустить их в своих файлах rspec или что-то еще, попробуйте это:

User.skip_callback(:save, :after, :generate_nick_name)
User.create!()

ПРИМЕЧАНИЕ: как только это будет сделано, если вы не находитесь в среде rspec, вы должны сбросить обратные вызовы:

User.set_callback(:save, :after, :generate_nick_name)

у меня отлично работает на рельсах 3.0.5

20 голосов
/ 26 ноября 2010

рельсы 3:

MyModel.send("_#{symbol}_callbacks") # list  
MyModel.reset_callbacks symbol # reset
16 голосов
/ 10 декабря 2015

Если цель состоит в том, чтобы просто вставить запись без обратных вызовов или проверок, и вы хотели бы сделать это, не прибегая к дополнительным гемам, не добавляя условные проверки, не используя RAW SQL, или каким-либо образом возившись с выходящим кодом, рассмотрите возможность «теневой объект», указывающий на вашу существующую таблицу БД. Вот так:

class ImportedPerson < ActiveRecord::Base
  self.table_name = 'people'
end

Это работает с каждой версией Rails, является поточно-ориентированным и полностью исключает все проверки и обратные вызовы без каких-либо изменений в существующем коде. Вы можете просто добавить эту декларацию класса прямо перед вашим фактическим импортом, и вам будет хорошо. Просто не забудьте использовать новый класс для вставки объекта, например:

ImportedPerson.new( person_attributes )
15 голосов
/ 11 марта 2009

Вы можете попробовать что-то подобное в своей модели Person:

after_save :something_cool, :unless => :skip_callbacks

def skip_callbacks
  ENV[RAILS_ENV] == 'development' # or something more complicated
end

РЕДАКТИРОВАТЬ: after_save не является символом, но это по крайней мере в 1000-й раз я пытался сделать его одним.

9 голосов
/ 03 апреля 2013

Вы можете использовать update_columns:

User.first.update_columns({:name => "sebastian", :age => 25})

Обновляет данные атрибуты объекта, не вызывая save, следовательно, пропуская проверки и обратные вызовы.

5 голосов
/ 13 апреля 2010

Похоже, что один из способов справиться с этим в Rails 2.3 (так как update_without_callbacks пропал и т. Д.) Будет использовать update_all, который является одним из методов, пропускающих обратные вызовы согласно разделу 12 Руководства по Rails проверки и обратные вызовы .

Кроме того, обратите внимание, что если вы что-то делаете в обратном вызове after_, который выполняет вычисления на основе многих ассоциаций (например, ассоциации has_many, где вы также делаете acceptpts_nested_attributes_for), вам потребуется перезагрузить ассоциацию, в случае если она является из сохранения, один из его участников был удален.

5 голосов
/ 11 марта 2009

Единственный способ предотвратить все обратные вызовы after_save - заставить первый вернуть false.

Возможно, вы могли бы попробовать что-то вроде (не проверено):

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
4 голосов
/ 25 февраля 2012

https://gist.github.com/576546

просто сбросьте этот monkey-patch в config / initializer / skip_callbacks.rb

тогда

Project.skip_callbacks { @project.save }

или тому подобное.

все благодарности автору

...