如何在 Rails 上的 Ruby 中引用模型 class 中的模型实例?

How do you refer an instance of a model in model class in Ruby on Rails?

我正在尝试在 Rails 中引用用户 class 的实例以进行地理编码

class User < ActiveRecord::Base
  geocoded_by :address

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

我要传递的是用户的当前实例。但是很明显,我最终传递的是 class 而不是实例,无法启动我的作业队列。

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

根据您当前编写的内容,GeocodeJob.perform_later(self) 将在加载 class 时调用,并且它的 return 值将用作传递给对 [= 的调用的参数12=]。正如你所说,这不是你想要的。

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

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

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

要在 job worker 上下文中访问 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

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