web-dev-qa-db-ja.com

ActiveRecordコールバックの実行を回避するにはどうすればよいですか?

After_saveコールバックを持つモデルがいくつかあります。通常はそれで問題ありませんが、開発データを作成するときなど、コールバックを実行せずにモデルを保存したい場合があります。それを行う簡単な方法はありますか?似たようなもの...

Person#save( :run_callbacks => false )

または

Person#save_without_callbacks

Railsのドキュメントを調べましたが、何も見つかりませんでした。しかし、私の経験では、Railsのドキュメントは必ずしもすべてを語っているわけではありません。

更新

次のようなモデルからコールバックを削除する方法を説明する ブログ投稿 を見つけました。

Foo.after_save.clear

その方法が文書化されている場所を見つけることができませんでしたが、うまくいくようです。

131
Ethan

このソリューションは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)

これは間違いなく、あなたが本当にコンソールで、またはいくつかのランダムなテストをしている間にのみ使いたいものです。お役に立てれば!

72
efalcao

update_column(Rails> = v3.1)またはupdate_columns(Rails> = 4.0)を使用して、コールバックと検証をスキップします。また、これらのメソッドでは、updated_atnot更新されます。

#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
218

更新しました:

@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でうまく動作します

28

Rails 3:

MyModel.send("_#{symbol}_callbacks") # list  
MyModel.reset_callbacks symbol # reset
20
guai

コールバックや検証なしで単純にレコードを挿入することが目標であり、追加のGemに頼らずに条件付きチェックを追加したり、RAW SQLを使用したり、何らかの方法で既存のコードを変更したりせずに挿入したい場合は、「シャドウ既存のdbテーブルを指すオブジェクト」。そのようです:

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

これは、Railsのすべてのバージョンで動作し、スレッドセーフであり、既存のコードを変更することなく、すべての検証とコールバックを完全に排除します。実際のインポートの直前にそのクラス宣言を投げることができます。次のように、新しいクラスを使用してオブジェクトを挿入することを忘れないでください。

ImportedPerson.new( person_attributes )
17
Brad Werth

Personモデルで次のようなものを試すことができます。

after_save :something_cool, :unless => :skip_callbacks

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

編集: after_saveはシンボルではありませんが、少なくとも1,000回は作成しようとしました。

16
Sarah Mei

update_columnsを使用できます:

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

保存を呼び出さずにオブジェクトの指定された属性を更新します。そのため、検証とコールバックをスキップします。

9
Luís Ramalho

すべての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
6
rfunduk

Rails 2.3(update_without_callbacksがなくなっているなど)でこれを処理する1つの方法のように見えますが、update_allを使用します。これは、コールバックをスキップするメソッドの1つです Railsのセクション12検証とコールバックのガイド

また、after_コールバックで何かを行っている場合、多くの関連付け(つまり、accepts_nested_attributes_forも行うhas_many assoc)に基づいて計算を行う場合、保存の一部として、関連付けをリロードする必要があることに注意してください。 、そのメンバーの1つが削除されました。

5
chrisrbailey

https://Gist.github.com/576546

このモンキーパッチをconfig/initializers/skip_callbacks.rbにダンプするだけです

それから

Project.skip_callbacks { @project.save }

など。

著者のすべての功績

4
fringd

Gemまたはプラグインを使用せずにRailsのすべてのバージョンで機能するソリューションは、単純に更新ステートメントを直接発行することです。例えば

ActiveRecord::Base.connection.execute "update table set foo = bar where id = #{self.id}"

これは、更新の複雑さに応じてオプションとなる場合もあれば、そうでない場合もあります。これは、たとえばwithin after_saveコールバック(コールバックを再トリガーせずに)からレコードのフラグを更新する場合に有効です。

3
Dave Smylie

ほとんどのup-voted回答は、場合によっては混乱を招くように見えるかもしれません。

次のように、コールバックをスキップする場合は、単純なifチェックだけを使用できます。

after_save :set_title, if: -> { !new_record? && self.name_changed? }
3
Aleks
# 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
1

Update_without_callbacksをRails 3に実装するプラグインを作成しました:

http://github.com/dball/skip_activerecord_callbacks

正しい解決策は、最初にコールバックを避けるためにモデルを書き直すことだと思いますが、短期的にはそれが実用的でない場合、このプラグインが役立つかもしれません。

1
Donald ball

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)
1
Steve Friedman

Railsでテストデータを作成するには、次のハックを使用します。

record = Something.new(attrs)
ActiveRecord::Persistence.instance_method(:create_record).bind(record).call

https://coderwall.com/p/y3yp2q/edit

1

Sneaky-save gemを使用できます: https://rubygems.org/gems/sneaky-save

これは、検証なしで関連付けを保存するのに役立ちません。モデルとは異なり、SQLクエリを直接挿入するため、エラー「created_atはnullにできません」がスローされます。これを実装するには、dbのすべての自動生成列を更新する必要があります。

1
Zinin Serge

Railsを使用している場合2.コールバックと検証を実行せずにSQLクエリを使用して列を更新できます。

YourModel.connection.execute("UPDATE your_models SET your_models.column_name=#{value} WHERE your_models.id=#{ym.id}")

Railsのどのバージョンでも動作するはずです。

1
oivoodoo

コールバックを完全に制御する必要がある場合、スイッチとして使用される別の属性を作成します。シンプルで効果的:

型:

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
1
tothemario

これらのいずれも、必要なことだけを行う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
kares

1つのオプションは、同じテーブルを使用して、そのような操作用に個別のモデルを作成することです。

class NoCallbacksModel < ActiveRecord::Base
  set_table_name 'table_name_of_model_that_has_callbacks'

  include CommonModelMethods # if there are
  :
  :

end

(同じアプローチは、検証をバイパスするために物事を簡単にするかもしれません)

ステファン

0
Stephan Wehner

別の方法は、コールバックの代わりに検証フックを使用することです。例えば:

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)
0
BoosterStage

存在する場合と存在しない場合があるオプションまたは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:同じテーブルで「異なるアクティブレコードモデル」を使用する

0
choonkeat

なぜ開発でこれを行えるようにしたいのですか?確かにこれは、無効なデータを使用してアプリケーションを構築していることを意味し、そのため、実稼働環境で予期しない動作をします。

Dev dbにデータを追加したい場合、faker gemを使用して有効なデータを作成し、dbにインポートして必要な数のレコードを作成するrakeタスクを作成しますが、ヒールがある場合は曲がってupdate_without_callbacksとcreate_without_callbacksがうまくいくと思いますが、Railsを自分の意志で曲げようとするときは、正当な理由があり、何をしているのかを自問してください本当にいいアイデアです。

0
nitecoder