【问题标题】:How do you refer an instance of a model in model class in Ruby on Rails?如何在 Ruby on Rails 的模型类中引用模型的实例?
【发布时间】:2026-02-04 23:45:01
【问题描述】:

我正在尝试在 Rails 中引用 User 类的实例进行地理编码

class User < ActiveRecord::Base
  geocoded_by :address

  after_validation GeocodeJob.perform_later(self), if: :address_changed?
end

我试图传递的是用户的当前实例。但是很明显,我最终传递的是类而不是实例,未能启动我的作业队列。

如何在模型回调中引用和传递用户实例?我知道我可以使用控制器使用实例变量,想知道我是否可以直接作为模型回调排队。

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 sidekiq resque


    【解决方案1】:

    根据您当前编写的内容,在加载类时将调用GeocodeJob.perform_later(self),并将其返回值用作传递给after_validation 调用的参数。正如你所说,这不是你想要的。

    相反,您可以传递一个符号来调用方法,如下所示:

    class User < ActiveRecord::Base
      geocoded_by :address
    
      after_validation :setup_geocode_job, if: :address_changed?
    
      def setup_gecode_job
        GeocodeJob.perform_later(self)
      end
    end
    

    这将通过调用模型的实例方法来完成您想要的操作,self 将成为模型实例。

    见:http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

    【讨论】:

    • 感谢@ihaztehcodez,这是正确的并且描述得很好。关于传递 id,我的理解是自从 Rails 4.2 当我们传递一个实例时,它是全局 id 而不是传递的对象。这不正确吗?
    • @PunjCoder 你说得对。似乎 sidekiq 的 github wiki 中的一些令人困惑的措辞让我对 rails 4.2 / ActiveJob 如何处理传递的实例感到困惑。感谢您指出这一点,我将从我的答案中删除那一点。
    【解决方案2】:

    直接使用 ActiveRecord 实例作为作业参数绝对是个坏主意。这里的主要问题是将 Ruby 对象序列化为某种可写格式(JSON、YAML 等)。阅读更多链接:

    要在作业工作者上下文中访问 ActiveRecord 实例,最好通过给定的 id 值找到记录:

    class GeocodeJob < ActiveJob::Base
      def perform(user_id)
        user = User.find user_id
        # do hard geocode work here
      end
    end
    

    从回调开始工作,例如:

    class User < ActiveRecord::Base
      geocoded_by :address
    
      after_validation if: :address_changed? do |user|
        GeocodeJob.perform_later(user.id)
      end
    end
    

    附言。我强烈建议使用after_commit 回调而不是after_validation,因为有时记录保存过程可能会在其他回调中被取消,或者如果会引发数据库问题。这里还有一件事——如果一个用户之前没有被保存过(比如User.create(...)),它的实例没有id值。

    【讨论】:

      最近更新 更多