无法将CollectionProxy对象传递给ActiveJob

时间:2015-08-22 08:52:30

标签: ruby-on-rails delayed-job rails-activejob

我需要在后台标记一组消息(我正在使用delayed_job gem),因为前景需要一些时间。我已经创建了一个ActiveJobMarkMessagesAsReadJob,并将usermessages个变量传递给了messages所有user {1}}。

// passing the values in the controller
@messages     = @conversation.messages
MarkMessagesAsReadJob.perform_later(current_user, @messages) 

在我的ActiveJob类中,我执行任务。

// MarkMessagesAsReadJob.rb
class MarkMessagesAsReadJob < ActiveJob::Base
  queue_as :default

  def perform(user, messages)
    messages.mark_as_read! :all, :for => user
  end
end

然而,当我尝试执行任务时,我收到了错误 ActiveJob :: SerializationError(不支持的参数类型:ActiveRecord :: Associations :: CollectionProxy):

我读到我们只能将支持的类型传递给ActiveJob,我认为它无法序列化CollectionProxy对象。我该如何解决/修复此问题?

PS:我考虑了

@messages.map { |message| MarkMessagesAsReadJob.perform_later(current_user, message) } 
然而,我认为逐一标记它们非常昂贵。

3 个答案:

答案 0 :(得分:6)

我认为简单的方法是将消息ID传递给perform_later()方法,例如:

控制器中的

@messages = @conversation.messages
message_ids = @messages.pluck(:id)
MarkMessagesAsReadJob.perform_later(current_user, message_ids) 

并在ActiveJob

中使用它
def perform(user, message_ids)
  messages = Message.where(id: ids)
  messages.mark_as_read! :all, :for => user
end

答案 1 :(得分:3)

因为作业将在稍后执行,我认为我们应该将id作为参数而不是集合

ActiveJob需要serialize参数,如果不支持参数类型,则抛出SerializationError http://api.rubyonrails.org/classes/ActiveJob/SerializationError.html

例如:

@message_ids = @conversation.messages.pluck(:id)
# use string if array is not supported
# @message_ids = @message_ids.join(", ")
MarkMessagesAsReadJob.perform_later(current_user, @message_ids) 

然后再次查询这些消息并标记

class MarkMessagesAsReadJob < ActiveJob::Base
  queue_as :default
  def perform(user, message_ids)
    # use string if array is not supported
    # message_ids = message_ids.split(",").map(&:to_i)
    messages = Message.where(id: message_ids) #change this to something else
    messages.mark_as_read! :all, :for => user
  end
end

未经测试,希望它没问题

答案 2 :(得分:1)

对于较大的数据集,传递message_ids是不切实际的。相反,请传递SQL消息:

@messages = @conversation.messages
MarkMessagesAsReadJob.perform_later(current_user, @messages.to_sql) 

然后从作业中查询它们:

class MarkMessagesAsReadJob < ActiveJob::Base
  queue_as :default
  def perform(user, messages_sql)
    messages = Message.find_by_sql(messages_sql) 
    messages.mark_as_read! :all, :for => user
  end
end