ActiveRecord 콜백을 실행하지 않으려면 어떻게해야합니까?


140

after_save 콜백이있는 모델이 있습니다. 일반적으로 문제는 없지만 개발 데이터를 만들 때와 같은 일부 상황에서는 콜백을 실행하지 않고 모델을 저장하려고합니다. 그렇게하는 간단한 방법이 있습니까? 비슷한 것 ...

Person#save( :run_callbacks => false )

또는

Person#save_without_callbacks

Rails 문서를 보았는데 아무것도 찾지 못했습니다. 그러나 내 경험상 Rails 문서가 항상 전체 이야기를 말하는 것은 아닙니다.

최신 정보

다음 과 같은 모델에서 콜백을 제거하는 방법을 설명하는 블로그 게시물 을 찾았 습니다 .

Foo.after_save.clear

그 방법이 문서화 된 곳을 찾을 수 없지만 작동하는 것 같습니다.


8
콜백에서 파괴적이거나 값 비싼 일을하는 경우 (이메일 보내기와 같이), 이것을 다른 곳으로 옮기고 컨트롤러 나 다른 곳에서 별도로 트리거하는 것이 좋습니다. 이런 식으로 개발 등에서 "실수로"트리거하지 않습니다.
ryanb

2
당신이 받아 들인 해결책이 나를 위해 작동하지 않습니다. 레일 3을 사용하고 있습니다. # <User : 0x10ae9b848>에 대해 정의되지 않은 메소드`update_without_callbacks '
Mohit Jain

yaa 그 블로그 게시물이 작동했습니다 ....
Mohit Jain


Foo.after_save.clear전체 모델에 대한 콜백을 제거 하지 않습니까? 그런 다음 복원을 어떻게 제안합니까?
Joshua Pinter

답변:


72

이 솔루션은 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)

이것은 확실히 콘솔에서만 또는 임의의 테스트를 수행하는 동안 사용하고 싶은 것입니다. 도움이 되었기를 바랍니다!


7
그것은 나를 위해 작동하지 않습니다. 레일 3을 사용하고 있습니다. # <User : 0x10ae9b848>에 대해 정의되지 않은 메소드`update_without_callbacks '
Mohit Jain

귀하의 제안은 작동하지 않지만 업데이트 부분에서 언급 된 블로그 게시물은 작동합니다.
Mohit Jain

유효성 검사도 건너 뜁니다.
Daniel Pietzsch

모든 버전의 Rails에 대한 다른 솔루션이 있습니다. 그것은 우리에게 잘 작동합니다. 내 블로그 게시물에서 확인하십시오 : railsguides.net/2014/03/25/skip-callbacks-in-tests
ka8725

224

사용 update_column(레일> = 3.1) 또는 update_columns(레일> = 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

2
그것은 2.x에서도 작동하는 것처럼 보이고 비슷하게 작동하는 다른 많은 방법이 있습니다 : guides.rubyonrails.org/…
rogerdpack

15
이 주소하지 않습니다 :create_without_callbacks:( 어떻게 그와 비슷한 뭔가를 실행할 수 있습니다 (Rails3에서 제거 Rails2에 근무)?.
nzifnab

@person어딘가에 컨트롤러의 변수가 있다고 가정하면 이 솔루션은 모델 클래스를 읽는 사람들이 콜백을 이해할 수 없음을 의미합니다. 그들은 after_create :something_cool"만든 후에 멋진 일이 발생합니다!" 를보고 생각할 것입니다. 실제로 모델 클래스를 이해하려면 모든 컨트롤러를 통해 grep하여 로직을 주입하기로 결정한 모든 작은 장소를 찾아야합니다. 나는 그것을 좋아하지 않는다> o <;;
Ziggy

1
교체 skip_callback ..., if: :skip_some_callbacksafter_create ..., unless: :skip_some_callbacksafter_create 제대로이를 실행합니다.
sakurashinken

28

업데이트 :

@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에서 잘 작동합니다.



19

콜백이나 유효성 검사없이 단순히 레코드를 삽입하는 것이 목표이며 추가 gem에 의존하지 않고 조건부 검사를 추가하거나 RAW SQL을 사용하거나 종료 코드를 사용하여 어떤 식 으로든 퍼징하지 않고 수행하려는 경우 "그림자"사용을 고려하십시오. 기존 DB 테이블을 가리키는 개체 " 이렇게 :

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

이것은 모든 Rails 버전에서 작동하며 스레드 안전하며 기존 코드를 수정하지 않고도 모든 유효성 검사 및 콜백을 완전히 제거합니다. 실제로 가져 오기 직전에 클래스 선언을 던질 수 있으며 잘 진행해야합니다. 다음과 같이 새 클래스를 사용하여 객체를 삽입하십시오.

ImportedPerson.new( person_attributes )

4
최고의 솔루션. 우아하고 간단합니다!
Rafael Oliveira

1
이것은 프로덕션에서만 사용되는 프로덕션 모델 객체를 기계로 오염시켜 선택적으로 콜백을 건너 뛰지 않고 데이터베이스 "이전"상태를 시뮬레이션하기 위해 테스트에서만하고 싶었던 일이기 때문에 정말 훌륭하게 작동했습니다.
Douglas Lovell

1
지금까지 가장 좋은 답변
robomc

1
기존 레일 제약 조건을 해결하는 방법을 보여주고 전체 객체 MVC가 실제로 어떻게 작동하는지 이해하는 데 도움이 되었기 때문에 찬성했습니다. 간단하고 깨끗합니다.
Michael Schmitz

17

Person 모델에서 다음과 같은 것을 시도해 볼 수 있습니다.

after_save :something_cool, :unless => :skip_callbacks

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

편집 : after_save는 기호가 아니지만 적어도 1,000 번 시도했습니다.


1
나는 이것이 이것이 가장 좋은 대답이라고 생각합니다. 이런 식으로 콜백을 건너 뛰는 시점을 결정하는 로직을 모델에서 사용할 수 있으며 비즈니스 로직을 엿보거나 캡슐화를 우회하는 곳마다 미친 코드 조각이 없습니다 send. KOODOS
Ziggy

10

당신은 사용할 수 있습니다 update_columns:

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

save를 호출하지 않고 객체의 지정된 속성을 업데이트하므로 유효성 검사 및 콜백을 건너 뜁니다.


7

모든 after_save 콜백을 방지하는 유일한 방법은 첫 번째 콜백이 false를 반환하도록하는 것입니다.

아마도 (unested)와 같은 것을 시도 할 수 있습니다.

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

1
나는 노력하는 것을 좋아한다. 스릴 타고.
Adamantish

테스트되었으며 작동합니다. 나는 이것이 매우 좋고 깨끗한 해결책이라고 생각합니다. 감사합니다!
kernification

5

(update_without_callbacks 등, 사라 때문에) 하나의 방법처럼 보인다는 따라 콜백을 건너 뛰고 방법 중 하나 인 update_all 사용하는 것, 레일 2.3에서이 문제를 처리하기 위해 검증 및 콜백에 레일 가이드 (12) .

또한 after_ 콜백에서 무언가를 수행하는 경우 많은 연관 (예 : has_many assoc, accepts_nested_attributes_for도 수행함)을 기반으로 계산을 수행하는 경우 저장의 일부로 연결을 다시로드해야합니다. , 회원 중 하나가 삭제되었습니다.



4

경우에 따라 대부분의 up-voted답변이 혼란스러워 보일 수 있습니다.

if다음과 같이 콜백을 건너 뛰려면 간단한 확인 만하면 됩니다.

after_save :set_title, if: -> { !new_record? && self.name_changed? }

3

gem 또는 플러그인을 사용하지 않고 모든 버전의 Rails에서 작동해야하는 솔루션은 단순히 업데이트 명령문을 발행하는 것입니다. 예 :

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

업데이트가 얼마나 복잡한 지에 따라 옵션이 될 수도 있고 아닐 수도 있습니다. 예를 들어 after_save 콜백 에서 (콜백을 다시 트리거하지 않고) 레코드의 플래그를 업데이트하는 데 효과적 입니다.


왜 다운 보트인지 확실하지 않지만, 여전히 위의 답변이 합법적이라고 생각합니다. 때때로 ActiveRecord 동작 문제를 피하는 가장 좋은 방법은 ActiveRecord 사용을 피하는 것입니다.
Dave Smylie

-1에 반대하는 원칙으로 공표되었습니다. 우리는 방금 긴 이야기와 함께 생산 문제가있어서 새로운 기록 (업데이트가 아님)을 만들어야했고 발사 콜백은 치명적이었습니다. 위의 모든 대답은 인정 여부에 관계없이 해킹이며 DB에가는 것이 가장 좋은 해결책이었습니다. 이에 대한 합법적 인 조건이 있습니다. 비록으로 SQL 인젝션을 조심해야한다 #{...}.
sinisterchipmunk

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

이 중 어느 것도 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

Rails 3에서 update_without_callbacks를 구현하는 플러그인을 작성했습니다.

http://github.com/dball/skip_activerecord_callbacks

올바른 해결책은 처음에는 콜백을 피하기 위해 모델을 다시 작성하는 것이지만 단기적으로 실용적이지 않으면이 플러그인이 도움이 될 수 있습니다.


1

Rails 2를 사용하는 경우 콜백 및 유효성 검사를 실행하지 않고 열을 업데이트하기 위해 SQL 쿼리를 사용할 수 있습니다.

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

나는 그것이 모든 레일 버전에서 작동해야한다고 생각합니다.


1

콜백을 완전히 제어해야 할 때 스위치로 사용되는 다른 속성을 만듭니다. 간단하고 효과적인 :

모델:

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

: 당신은 보석 비열한-저장 사용할 수 있습니다 https://rubygems.org/gems/sneaky-save .

이는 유효성 검증없이 연관을 저장하는 데 도움이되지 않습니다. 모델과 달리 SQL 쿼리를 직접 삽입하기 때문에 'created_at null을 할 수 없습니다'라는 오류가 발생합니다. 이를 구현하려면 자동 생성 된 db 열을 모두 업데이트해야합니다.


1

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)

0

왜 개발에서 이것을 할 수 있기를 원합니까? 분명히 이것은 유효하지 않은 데이터로 응용 프로그램을 빌드한다는 것을 의미하므로 프로덕션에서 예상하지 않은 이상하게 작동합니다.

dev db를 데이터로 채우려면 더 나은 방법은 faker gem을 사용하여 유효한 데이터를 작성하고 원하는 수만큼 또는 적은 수의 레코드를 생성하는 db로 가져 오는 레이크 작업을 작성하는 것입니다. 그것을 구부리고 합당한 이유가 있다고 생각합니다 .update_without_callbacks 및 create_without_callbacks는 잘 작동하지만, 당신의 의지에 레일을 구부리려고 할 때, 당신에게 좋은 이유가 있다고 생각하십시오.


콜백없이 유효성 검사없이 저장하려고하지 않습니다. 내 응용 프로그램은 콜백을 사용하여 정적 HTML을 파일 시스템에 작성합니다 (CMS와 같은 종류). 개발자 데이터를로드하는 동안 그렇게하고 싶지 않습니다.
Ethan

그냥 생각이었는데, 과거에 나는 이런 종류의 질문을 보았을 때 나쁜 이유로 물건을 돌아 다니려고합니다.
nitecoder 2018 년

0

하나의 옵션은 동일한 테이블을 사용하여 이러한 조작에 대한 별도의 모델을 갖는 것입니다.

class NoCallbacksModel < ActiveRecord::Base
  set_table_name 'table_name_of_model_that_has_callbacks'

  include CommonModelMethods # if there are
  :
  :

end

동일한 접근 방식으로 유효성 검사를 우회하기가 더 쉬워 질 수 있습니다.

스테판


0

다른 방법은 콜백 대신 유효성 검사 후크를 사용하는 것입니다. 예를 들면 다음과 같습니다.

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)

3
이것은 나쁜 생각처럼 보입니다. 의도 된 목적으로 물건을 사용해야합니다. 마지막으로 원하는 것은 유효성 검사에 부작용이있는 것입니다.
chug2k 2010 년

0

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

사용자 정의 콜백 들어, 사용 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_callbacksvirtual 속성을로 설정하십시오 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.

-5

가장 깨끗한 방법은 아니지만 Rails 환경을 확인하는 조건에서 콜백 코드를 래핑 할 수 있습니다.

if Rails.env == 'production'
  ...
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.