After_saveコールバックを持つモデルがいくつかあります。通常はそれで問題ありませんが、開発データを作成するときなど、コールバックを実行せずにモデルを保存したい場合があります。それを行う簡単な方法はありますか?似たようなもの...
Person#save( :run_callbacks => false )
または
Person#save_without_callbacks
Railsのドキュメントを調べましたが、何も見つかりませんでした。しかし、私の経験では、Railsのドキュメントは必ずしもすべてを語っているわけではありません。
更新
次のようなモデルからコールバックを削除する方法を説明する ブログ投稿 を見つけました。
Foo.after_save.clear
その方法が文書化されている場所を見つけることができませんでしたが、うまくいくようです。
このソリューションはRails 2のみです。
これを調査したところ、解決策があると思います。使用できるActiveRecordプライベートメソッドは2つあります。
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)
これは間違いなく、あなたが本当にコンソールで、またはいくつかのランダムなテストをしている間にのみ使いたいものです。お役に立てれば!
update_column
(Rails> = v3.1)またはupdate_columns
(Rails> = 4.0)を使用して、コールバックと検証をスキップします。また、これらのメソッドでは、updated_at
はnot更新されます。
#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
更新しました:
@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)
Rails 3.0.5でうまく動作します
Rails 3:
MyModel.send("_#{symbol}_callbacks") # list
MyModel.reset_callbacks symbol # reset
コールバックや検証なしで単純にレコードを挿入することが目標であり、追加のGemに頼らずに条件付きチェックを追加したり、RAW SQLを使用したり、何らかの方法で既存のコードを変更したりせずに挿入したい場合は、「シャドウ既存のdbテーブルを指すオブジェクト」。そのようです:
class ImportedPerson < ActiveRecord::Base
self.table_name = 'people'
end
これは、Railsのすべてのバージョンで動作し、スレッドセーフであり、既存のコードを変更することなく、すべての検証とコールバックを完全に排除します。実際のインポートの直前にそのクラス宣言を投げることができます。次のように、新しいクラスを使用してオブジェクトを挿入することを忘れないでください。
ImportedPerson.new( person_attributes )
Personモデルで次のようなものを試すことができます。
after_save :something_cool, :unless => :skip_callbacks
def skip_callbacks
ENV[Rails_ENV] == 'development' # or something more complicated
end
編集: after_saveはシンボルではありませんが、少なくとも1,000回は作成しようとしました。
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がなくなっているなど)でこれを処理する1つの方法のように見えますが、update_allを使用します。これは、コールバックをスキップするメソッドの1つです Railsのセクション12検証とコールバックのガイド 。
また、after_コールバックで何かを行っている場合、多くの関連付け(つまり、accepts_nested_attributes_forも行うhas_many assoc)に基づいて計算を行う場合、保存の一部として、関連付けをリロードする必要があることに注意してください。 、そのメンバーの1つが削除されました。
https://Gist.github.com/576546
このモンキーパッチをconfig/initializers/skip_callbacks.rbにダンプするだけです
それから
Project.skip_callbacks { @project.save }
など。
著者のすべての功績
Gemまたはプラグインを使用せずにRailsのすべてのバージョンで機能するソリューションは、単純に更新ステートメントを直接発行することです。例えば
ActiveRecord::Base.connection.execute "update table set foo = bar where id = #{self.id}"
これは、更新の複雑さに応じてオプションとなる場合もあれば、そうでない場合もあります。これは、たとえばwithin after_saveコールバック(コールバックを再トリガーせずに)からレコードのフラグを更新する場合に有効です。
ほとんどのup-voted
回答は、場合によっては混乱を招くように見えるかもしれません。
次のように、コールバックをスキップする場合は、単純なif
チェックだけを使用できます。
after_save :set_title, if: -> { !new_record? && self.name_changed? }
# 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
Update_without_callbacksをRails 3に実装するプラグインを作成しました:
http://github.com/dball/skip_activerecord_callbacks
正しい解決策は、最初にコールバックを避けるためにモデルを書き直すことだと思いますが、短期的にはそれが実用的でない場合、このプラグインが役立つかもしれません。
Rails 4のソリューションが必要だったので、これを思いつきました。
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
どのモデルでも:
include SaveWithoutCallbacks
その後、次のことができます。
record.save_without_callbacks
または
Model::WithoutCallbacks.create(attributes)
Railsでテストデータを作成するには、次のハックを使用します。
record = Something.new(attrs)
ActiveRecord::Persistence.instance_method(:create_record).bind(record).call
Sneaky-save gemを使用できます: https://rubygems.org/gems/sneaky-save 。
これは、検証なしで関連付けを保存するのに役立ちません。モデルとは異なり、SQLクエリを直接挿入するため、エラー「created_atはnullにできません」がスローされます。これを実装するには、dbのすべての自動生成列を更新する必要があります。
Railsを使用している場合2.コールバックと検証を実行せずにSQLクエリを使用して列を更新できます。
YourModel.connection.execute("UPDATE your_models SET your_models.column_name=#{value} WHERE your_models.id=#{ym.id}")
Railsのどのバージョンでも動作するはずです。
コールバックを完全に制御する必要がある場合、スイッチとして使用される別の属性を作成します。シンプルで効果的:
型:
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
これらのいずれも、必要なことだけを行う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で動作します
1つのオプションは、同じテーブルを使用して、そのような操作用に個別のモデルを作成することです。
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
TLDR:同じテーブルで「異なるアクティブレコードモデル」を使用する
なぜ開発でこれを行えるようにしたいのですか?確かにこれは、無効なデータを使用してアプリケーションを構築していることを意味し、そのため、実稼働環境で予期しない動作をします。
Dev dbにデータを追加したい場合、faker gemを使用して有効なデータを作成し、dbにインポートして必要な数のレコードを作成するrakeタスクを作成しますが、ヒールがある場合は曲がってupdate_without_callbacksとcreate_without_callbacksがうまくいくと思いますが、Railsを自分の意志で曲げようとするときは、正当な理由があり、何をしているのかを自問してください本当にいいアイデアです。