简体   繁体   中英

Rails fires N+1 queries for polymorphic associations

I am using Rails 5.0.1, and am really confused about the following problem. I have few models with polymorphic associations.

class Container < ApplicationRecord
  has_many :steps, as: 'parent', dependent: :destroy
end

class Step < ApplicationRecord
  belongs_to :parent, polymorphic: true
  belongs_to :implementation, polymorphic: true
end

class FirstStep < ApplicationRecord
  has_one :step, as: 'implementation'
  has_many :params, dependent: :destroy
end

class SecondStep < ApplicationRecord
  has_one :step, as: 'implementation'
  has_many :headers, dependent: :destroy
end

class Param < ApplicationRecord
  belongs_to :first_step
end

class Header < ApplicationRecord
  belongs_to :second_step
end

A step associates to an implementation ( FirstStep , SecondStep ). In addition to it, a container can also be a step 's implementation. I'm using Active Model Serializers to serialize the model info to JSON. Following is the related code to serializers.

class StepSerializer < ActiveModel::Serializer
  attributes :id, :implementation_type, :implementation_id, :active, :position

  belongs_to :implementation
end

class FirstStepSerializer < ActiveModel::Serializer
  attributes :name, :params_attributes

  def params_attributes
    object.params.map { |p| ParamSerializer.new(p).attributes }
  end
end

class SecondStepSerializer < ActiveModel::Serializer
  attributes :id, :title, :headers_attributes

  def headers_attributes
    object.headers.map { |p| HeaderSerializer.new(p).attributes }
  end
end

class ParamSerializer < ActiveModel::Serializer
  attributes :id
end

class HeaderSerializer < ActiveModel::Serializer
  attributes :id
end

The implementations of step model can have different attributes, as specified in the model. The problem is, when I write

render json: container.steps

it fires N+1 queries to get the results. How do I optimize it?

Edit 1

Inspired by this answer , I tried to separate objects by their implementation_type , and it worked. What I did was:

# my controller action
def index
  steps = []

  steps += container.steps.where(implementation_type: 'FirstStep').includes(implementation: [:params])
  steps += container.steps.where(implementation_type: 'SecondStep').includes(implementation: [:headers])

  render json: steps
end

This prevented the N+1 queries for fetching params and headers , but it doesn't work if a step is a container .

Change your FirstStepSerializer and SecondStepSerializer serializer like following

class FirstStepSerializer < ActiveModel::Serializer
  attributes :name
  has_many :params, :serializer => ParamSerializer
end

class SecondStepSerializer < ActiveModel::Serializer
  attributes :id, :title
  has_many :headers, :serializer => HeaderSerializer
end

This might help

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM