Ruby on Rails——堆栈级别太深

Posted

技术标签:

【中文标题】Ruby on Rails——堆栈级别太深【英文标题】:Ruby on Rails -- Stack level too deep 【发布时间】:2018-07-02 10:18:43 【问题描述】:

型号:

after_save :set_correct_post_type

def set_correct_post_type
  if self.document.present?
    if (find_mime_type(self.document.original_filename) == "application/vnd.openxmlformats-officedocument.presentationml.presentation") || (find_mime_type(self.document.original_filename) == "application/vnd.ms-powerpoint")
      self.update_attributes(:post_type => 3)
    elsif (find_mime_type(self.document.original_filename) == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ) || (find_mime_type(self.document.original_filename) == "application/msword") || (find_mime_type(self.document.original_filename) == "application/pdf")
      self.update_attributes(:post_type => 2)
    elsif (MIME::Types.type_for(self.document.original_filename).first.content_type == "image/png") || (MIME::Types.type_for(self.document.original_filename).first.content_type =="image/jpeg") || (MIME::Types.type_for(self.document.original_filename).first.content_type == "image/jpg")
      self.update_attributes(:post_type => 5)
    end
  end #line17
end

日志:

  (0.3ms)  rollback transaction
Completed 500 Internal Server Error in 1939ms (ActiveRecord: 37.5ms)

SystemStackError (stack level too deep):
  app/models/teacher_post.rb:17:in `set_correct_post_type'
  app/models/teacher_post.rb:17:in `set_correct_post_type'
  app/models/teacher_post.rb:17:in `set_correct_post_type'
  app/models/teacher_post.rb:17:in `set_correct_post_type'
  app/models/teacher_post.rb:17:in `set_correct_post_type'
  app/models/teacher_post.rb:17:in `set_correct_post_type'
  app/models/teacher_post.rb:17:in `set_correct_post_type'

然后它一次又一次地重复最后一行,直到我手动停止它。谁能告诉我我做错了什么?

【问题讨论】:

哪一行在app/models/teacher_post.rb:17 @JagdeepSingh 我已经在模型代码中注释了第 17 行 【参考方案1】:

“堆栈级别太深错误”的原因是因为您在after_save回调中调用update_attributes,这将再次调用回调after_save,再次调用update_attributes,以此类推...

改成如下:

before_save :set_correct_post_type

def set_correct_post_type
  if self.document.present?
    if (find_mime_type(self.document.original_filename) == "application/vnd.openxmlformats-officedocument.presentationml.presentation") || (find_mime_type(self.document.original_filename) == "application/vnd.ms-powerpoint")
      self.post_type = 3
    elsif (find_mime_type(self.document.original_filename) == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ) || (find_mime_type(self.document.original_filename) == "application/msword") || (find_mime_type(self.document.original_filename) == "application/pdf")
      self.post_type = 2
    elsif (MIME::Types.type_for(self.document.original_filename).first.content_type == "image/png") || (MIME::Types.type_for(self.document.original_filename).first.content_type =="image/jpeg") || (MIME::Types.type_for(self.document.original_filename).first.content_type == "image/jpg")
      self.post_type = 5
    end
  end
end

【讨论】:

【参考方案2】:

当您调用 update_attributes 时,会调用回调,因为您的方法 set_correct_post_type 设置为“after_save”,您会以循环结束。

您调用 save 方法触发 set_correct_post_type 方法调用 update_attributes 触发 set_correct_post_type 方法调用 update_attributes...

如果您不想在方法中触发回调,请查看 update_columns 而不是 update_attributes。 考虑使用 before_save 而不是 after save 设置属性。

【讨论】:

【参考方案3】:

如果您有自定义代码,但没有任何 gem 等,请避免使用 rails 回调。我建议使用 Serice Object ex。 CreateTeacherPost 将创建一个帖子,在一个事务中使用参数、类型等进行魔术。这样你就可以避免像下面这样的问题,并且在没有回调魔法的情况下你将永远知道发生了什么。

但如果你真的想使用这种模式,它会进入一个无限循环,因为每个 update_attributes 都在调用 after_save!回调方法。您可以使用update_column 方法或before_save 回调并直接使用self.post_type=number 设置属性。 但是第一次会调用 SQL 更新第二次,没有理由这样做。

还有一个 :) 如果你必须/想要使用 after 回调,最好使用 after_commit 回调。它更安全。

【讨论】:

以上是关于Ruby on Rails——堆栈级别太深的主要内容,如果未能解决你的问题,请参考以下文章

Rails:before_save - 堆栈级别太深

Ruby数组与哈希测试中的堆栈级别太深

如何在应用程序级别加密 ruby​​ on rails 中的完整路由

如何在Ruby on Rails的应用程序级别添加toastr JS通知

SystemStackError - 堆栈级别太深;在 Rspec 测试中,使用acts_as_audited、Rspec、数据库清理器

乘客(Ruby on Rails)中的301重定向从根域到www子域?