У меня есть модели с обратными вызовами after_save. Обычно это нормально, но в некоторых ситуациях, например при создании данных для разработки, я хочу сохранить модели без выполнения обратных вызовов. Есть простой способ сделать это? Что-то вроде ...
Person#save( :run_callbacks => false )
или
Person#save_without_callbacks
Я просмотрел документацию Rails и ничего не нашел. Однако, по моему опыту, документы Rails не всегда рассказывают всю историю.
ОБНОВИТЬ
Я нашел сообщение в блоге, в котором объясняется, как можно удалить обратные вызовы из такой модели:
Foo.after_save.clear
Я не смог найти, где задокументирован этот метод, но, похоже, он работает.
Foo.after_save.clear
удалял обратные вызовы для всей модели? И как тогда вы предлагаете их восстановить?Ответы:
Это решение только для Rails 2.
Я только что исследовал это и думаю, что у меня есть решение. Вы можете использовать два частных метода ActiveRecord:
Вам нужно будет использовать send для вызова этих методов. Примеры:
p = Person.new(:name => 'foo') p.send(:create_without_callbacks) p = Person.find(1) p.send(:update_without_callbacks)
Это определенно то, что вы действительно захотите использовать только в консоли или при выполнении некоторых случайных тестов. Надеюсь это поможет!
источник
Используйте
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
источник
:create_without_callbacks
:( Как я могу запустить что-то подобное? (Работает в Rails2, удалено в Rails3).@person
, что где-то в контроллере есть переменная, это решение означает, что люди, читающие ваш класс модели, не смогут понять обратные вызовы. Они увидятafter_create :something_cool
и подумают: «Отлично, после создания происходит что-то классное!». Чтобы на самом деле понять ваш модельный класс, им придется перебирать все ваши контроллеры, ища все маленькие места, где вы решили внедрить логику. Мне это не нравится> o <;;skip_callback ..., if: :skip_some_callbacks
на,after_create ..., unless: :skip_some_callbacks
чтобы правильно запустить это с помощью after_create.Обновлено:
Решение @Vikrant Chaudhary кажется лучше:
#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)
Подход 2: если вы хотите пропустить их в своих файлах rspec или чем-то еще, попробуйте следующее:
User.skip_callback(:save, :after, :generate_nick_name) User.create!()
ПРИМЕЧАНИЕ: как только это будет сделано, если вы не находитесь в среде rspec, вам следует сбросить обратные вызовы:
User.set_callback(:save, :after, :generate_nick_name)
у меня отлично работает на рельсах 3.0.5
источник
Если цель состоит в том, чтобы просто вставить запись без обратных вызовов или проверок, и вы хотели бы сделать это, не прибегая к дополнительным драгоценным камням, добавляя условные проверки, используя RAW SQL или каким-либо образом изменяя существующий код, рассмотрите возможность использования «тени объект ", указывающий на существующую таблицу БД. Вот так:
class ImportedPerson < ActiveRecord::Base self.table_name = 'people' end
Это работает со всеми версиями Rails, является потокобезопасным и полностью исключает все проверки и обратные вызовы без каких-либо изменений в существующем коде. Вы можете просто добавить это объявление класса прямо перед фактическим импортом, и все будет в порядке. Просто не забудьте использовать свой новый класс для вставки объекта, например:
источник
рельсы 3:
MyModel.send("_#{symbol}_callbacks") # list MyModel.reset_callbacks symbol # reset
источник
reset_callbacks
нет:after_save
, а скорее:save
. apidock.com/rails/v3.0.9/ActiveSupport/Callbacks/ClassMethods/…Вы можете попробовать что-то подобное в своей модели Person:
after_save :something_cool, :unless => :skip_callbacks def skip_callbacks ENV[RAILS_ENV] == 'development' # or something more complicated end
РЕДАКТИРОВАТЬ: after_save не является символом, но, по крайней мере, в тысячный раз я пытался сделать его одним.
источник
send
. KOODOSВы можете использовать
update_columns
:User.first.update_columns({:name => "sebastian", :age => 25})
источник
Единственный способ предотвратить все обратные вызовы 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
источник
Похоже, что одним из способов справиться с этим в Rails 2.3 (поскольку update_without_callbacks больше нет и т. Д.) Было бы использование update_all, который является одним из методов, который пропускает обратные вызовы в соответствии с разделом 12 руководства Rails по проверкам и обратным вызовам .
Также обратите внимание, что если вы что-то делаете в своем обратном вызове after_, который выполняет расчет на основе многих ассоциаций (например, has_many assoc, где вы также принимаете accept_nested_attributes_for), вам нужно будет перезагрузить ассоциацию, в случае, если как часть сохранения , один из ее участников был удален.
источник
В
up-voted
некоторых случаях ответ может показаться запутанным.Вы можете использовать простую
if
проверку, если хотите пропустить обратный вызов, например:after_save :set_title, if: -> { !new_record? && self.name_changed? }
источник
https://gist.github.com/576546
просто сбросьте этот патч обезьяны в config / initializers / skip_callbacks.rb
тогда
Project.skip_callbacks { @project.save }
или т.п.
все кредиты автору
источник
Решение, которое должно работать во всех версиях Rails без использования гема или плагина, - это просто напрямую выдавать операторы обновления. например
ActiveRecord::Base.connection.execute "update table set foo = bar where id = #{self.id}"
Это может (а может и не быть) вариант в зависимости от сложности вашего обновления. Это хорошо работает , например , для флагов корректировки на запись из в качестве after_save обратного вызова (без обратного вызова перезапуска).
источник
#{...}
.Мне нужно было решение для Rails 4, поэтому я придумал следующее:
приложение / модели / проблемы / 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
в любой модели:
include SaveWithoutCallbacks
тогда ты можешь:
или
источник
# 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
источник
Ничто из этого не указывает на
without_callbacks
плагин, который просто делает то, что вам нужно ...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 работает с Rails 2.x
источник
Я написал плагин, который реализует update_without_callbacks в Rails 3:
http://github.com/dball/skip_activerecord_callbacks
Я думаю, что правильное решение - это переписать ваши модели, чтобы в первую очередь избежать обратных вызовов, но если это непрактично в ближайшем будущем, этот плагин может помочь.
источник
Если вы используете Rails 2. Вы можете использовать SQL-запрос для обновления столбца без выполнения обратных вызовов и проверок.
YourModel.connection.execute("UPDATE your_models SET your_models.column_name=#{value} WHERE your_models.id=#{ym.id}")
Думаю, он должен работать в любых версиях рельсов.
источник
Когда мне нужен полный контроль над обратным вызовом, я создаю другой атрибут, который используется в качестве переключателя. Просто и эффективно:
Модель:
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
Контрольная работа:
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
источник
Для создания тестовых данных в Rails вы используете этот прием:
record = Something.new(attrs) ActiveRecord::Persistence.instance_method(:create_record).bind(record).call
https://coderwall.com/p/y3yp2q/edit
источник
Вы можете использовать драгоценный камень скрытого сохранения: https://rubygems.org/gems/sneaky-save .
Обратите внимание, что это не может помочь в сохранении ассоциаций без проверок. Он выдает ошибку created_at cannot be null, поскольку он напрямую вставляет запрос sql в отличие от модели. Чтобы реализовать это, нам нужно обновить все автоматически сгенерированные столбцы db.
источник
Почему вы хотите иметь возможность делать это в процессе разработки? Конечно, это будет означать, что вы создаете свое приложение с недопустимыми данными, и поэтому оно будет вести себя странно, а не так, как вы ожидаете в производственной среде.
Если вы хотите заполнить свой dev db данными, лучшим подходом будет создание rake-задачи, которая использовала faker gem для создания достоверных данных и импорта их в db, создавая столько или несколько записей, сколько вы хотите, но если вы на пятке Я склонен к этому, и у меня есть веская причина, я предполагаю, что update_without_callbacks и create_without_callbacks будут работать нормально, но когда вы пытаетесь согнуть рельсы по своей воле, спросите себя, у вас есть веская причина и действительно ли то, что вы делаете, хорошая идея.
источник
Один из вариантов - иметь отдельную модель для таких манипуляций, используя ту же таблицу:
class NoCallbacksModel < ActiveRecord::Base set_table_name 'table_name_of_model_that_has_callbacks' include CommonModelMethods # if there are : : end
(Тот же подход может облегчить обход проверок)
Стефан
источник
Другой способ - использовать хуки проверки вместо обратных вызовов. Например:
class Person < ActiveRecord::Base validate_on_create :do_something def do_something "something clever goes here" end end
Таким образом, вы можете получить do_something по умолчанию, но вы можете легко переопределить его с помощью:
@person = Person.new @person.save(false)
источник
То, что должно работать со всеми версиями,
ActiveRecord
вне зависимости от опций или методов activerecord, которые могут существовать или не существовать.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
TL; DR: используйте "другую модель активной записи" для той же таблицы
источник
Для настраиваемых обратных вызовов используйте
attr_accessor
иunless
в обратном вызове.Определите свою модель следующим образом:
class Person << ActiveRecord::Base attr_accessor :skip_after_save_callbacks after_save :do_something, unless: :skip_after_save_callbacks end
А затем, если вам нужно сохранить запись, не
after_save
затрагивая заданные вами обратные вызовы, установите дляskip_after_save_callbacks
виртуального атрибута значениеtrue
.person.skip_after_save_callbacks #=> nil person.save # By default, this *will* call `do_something` after saving. person.skip_after_save_callbacks = true person.save # This *will not* call `do_something` after saving. person.skip_after_save_callbacks = nil # Always good to return this value back to its default so you don't accidentally skip callbacks.
источник
Я столкнулся с той же проблемой, когда хотел запустить задачу Rake, но без выполнения обратных вызовов для каждой сохраняемой записи. Это сработало для меня (Rails 5), и должно работать почти для каждой версии Rails:
class MyModel < ApplicationRecord attr_accessor :skip_callbacks before_create :callback1 before_update :callback2 before_destroy :callback3 private def callback1 return true if @skip_callbacks puts "Runs callback1" # Your code end def callback2 return true if @skip_callbacks puts "Runs callback2" # Your code end # Same for callback3 and so on.... end
Это работает так, что он просто возвращает true в первой строке метода, skip_callbacks имеет значение true, поэтому остальная часть кода метода не выполняется. Чтобы пропустить обратные вызовы, вам просто нужно установить skip_callbacks в true перед сохранением, созданием, уничтожением:
rec = MyModel.new() # Or Mymodel.find() rec.skip_callbacks = true rec.save
источник
Не самый чистый способ, но вы можете обернуть код обратного вызова в условие, которое проверяет среду Rails.
if Rails.env == 'production' ...
источник