Нетерпеливая загрузка ассоциаций с активными сериализаторами моделей

Задний план

У меня есть приложение 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 ...

Проблема

Все это работает, за исключением того, что ничего не загружается. Таким образом, для каждого запроса выполняется множество обращений к базе данных. Для каждой недели он делает отдельный запрос для дней этой недели. И для каждого дня он делает отдельный запрос для своих рабочих_периодов, промежуточных итогов и корректировок.




Ответы (2)


Одним из решений является определение собственного метода weeks в TimecardSerializer. Оттуда вы можете .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

Все запросы по-прежнему будут отображаться в журнале, но большинство из них будут кешированными запросами, а не реальными.

person Ryan    schedule 08.08.2013
comment
Это нормально для загрузки одного ресурса, но не будет работать, если этот сериализатор используется для возврата списка Timecard. В частности, weeks не будет жадно загружаться, хотя ассоциации weeks будут. - person hjdivad; 13.08.2015

У меня была аналогичная проблема. Я исправил это в своем контроллере. Мне нравится идея поместить его в сериализатор, но наличие его в контроллере также улавливает проблему n + 1 недель, созданную ArraySerializer.

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

а также

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

теперь следует жадно загружать и ограничивать запрос всего шестью обращениями к БД.

person James    schedule 20.08.2013
comment
вы не можете вызывать include для экземпляра, вы должны сделать Timecard.where(id: params[:id]).includes(...) - person Pere Joan Martorell; 07.05.2019