17

背景

我有一个具有深度嵌套关联的 Rails 应用程序。

                          .-< WorkPeriod
Timecard -< Week -< Day -<--< Subtotal
                          `-< Adjustment

-<  (has many)

我正在使用Active Model Serializer来构建 API。

在客户端,我想一次性加载一张考勤卡及其所有关联。

目前我的序列化程序看起来像这样,

class TimecardSerializer < ActiveModel::Serializer
  embed :ids, include: true
  has_many :weeks
end
class WeekSerializer < ActiveModel::Serializer
  embed :ids, include: true
  has_many :days
end
# ... etc ...

问题

这一切都可以找到,除了没有任何东西被急切加载。所以它最终会为每个请求对数据库进行大量调用。对于每一周,它都会单独请求该周的天数。并且对于每一天,它都会单独请求它的 work_periods、小计和调整。

4

2 回答 2

13

一种解决方案是在 TimecardSerializer 上定义自己的weeks方法。从那里您可以.includes()急切加载所有想要加载的关联。

class TimecardSerializer < ActiveModel::Serializer
  embed :ids, include: true
  has_many :weeks

  def weeks
    object.weeks.includes(days: [:sub_totals, :work_periods, :adjustments])
  end
end

所有查询仍将显示在日志中,但大多数将是缓存查询而不是真实查询。

于 2013-08-08T19:38:17.243 回答
9

我有一个类似的问题。我将它固定在我的控制器中。我喜欢把它放在序列化器中的想法,但是把它放在控制器中也会捕获由 ArraySerializer 创建的 n+1 周问题。

Timecard.find(params[:id]).includes(weeks: [{ days: [:sub_totals, :work_periods, :adjustments] }])

Timecard.includes(weeks: [{ days: [:sub_totals, :work_periods, :adjustments] }])

现在应该急切加载并将查询限制为仅六个 db 命中。

于 2013-08-20T13:00:32.683 回答