问题
Background
I have a rails application with deeply nested associations.
.-< WorkPeriod
Timecard -< Week -< Day -<--< Subtotal
`-< Adjustment
-< (has many)
I'm using Active Model Serializer to build out the API.
On the client side I want to load a timecard and all it's associations in one shot.
Currently my serializers look like this,
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 ...
Problem
This all works find, except nothing gets eager-loaded. So it ends up making lots of calls to the database for each request. For each week, it makes a separate request for the days in that week. And for each day, it makes a separate request for it's work_periods, subtotals, and adjustments.
回答1:
One solution is to define your own weeks
method on the TimecardSerializer. From there you can .includes()
all the associations you want to eager load.
class TimecardSerializer < ActiveModel::Serializer
embed :ids, include: true
has_many :weeks
def weeks
object.weeks.includes(days: [:sub_totals, :work_periods, :adjustments])
end
end
All the queries will still show up in the log but most will be a cached query instead of a real one.
回答2:
I had a similar issue. I fixed it in my controller. I like the idea of putting it in the serializer, but having it in the controller catches the n+1 weeks problem created by the ArraySerializer too.
Timecard.find(params[:id]).includes(weeks: [{ days: [:sub_totals, :work_periods, :adjustments] }])
and
Timecard.includes(weeks: [{ days: [:sub_totals, :work_periods, :adjustments] }])
should now eager load and limit the query to just six db hits.
来源:https://stackoverflow.com/questions/18134649/eager-load-associations-with-active-model-serializers