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.