panko_serializer
panko_serializer copied to clipboard
Trouble detecting polymorphic relations
Hi @yosiat, As I was migrating from AMS to this excellent serializer, I keep running into an error where Panko is unable to determine the serializer for a polymorphic relation:
class DrinkSerializer < Panko::Serializer
has_one :drinkable
# Since it belongs to either Tea or Coffee, I can't use 'serializer: option'
# AMS detected it automatically, so how do I tell Panko which one to use?
end
I couldn't find polymorphic relations mentioned in the docs, so let me know the best way to work with polymorphic relations in Panko.
Thank you!
Ideally, Panko can determine it, or it can provide an option like fast_jsonapi folks provide:
has_many :targets, polymorphic: { Person => :person, Group => :group }
@oyeanuj when panko handles associations it's simple as calling a method on the subject, for example drink.drinkable
so there is no something special there.
If you can provide me with an example with this template - https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_gem.rb I could find out the root cause and create a PR to fix it.
EDIT:
Now I understand your issue here, the problem is the relation is polymorphic and you don't know which serializer to use?
@yosiat: I think I can clarify - the issue is that the polymorphic relationships aren't being discovered by Panko even though ActiveRecord knows more. So drink.drinkable
is leading to a result but Panko throws a runtime error saying there is no serializer for it.
My guess is it is looking for a DrinkableSerializer rather than treating them as polymorphic? If it is hard for Panko to tell, then an option like in Fast_JsonAPI above would be helpful.
@yosiat I wasn't sure how to create a test case for Panko from that template but I did make a template with a polymorphic relation which almost gets us there. Here it is and hope this is helpful:
https://gist.github.com/oyeanuj/b10cc51de54239543d21a66dcf9a4b39
@oyeanuj Like @yosiat said, you can just calls a method defined in each polymorphic model.
class MembershipSerializer < Panko::Serializer
attributes :id, :identity, :created_at, :updated_at
def identity
object.identity.serialize(
except: [:updated_at, :created_at]
)
end
end
class Membership < ApplicationRecord
belongs_to :identity, polymorphic: true
end
class Guest < ApplicationRecord
has_many :memberships, as: :identity, dependent: :destroy
def serialize(options={})
GuestSerializer.new(options).serialize(self)
end
end
class User < ApplicationRecord
has_many :memberships, as: :identity, dependent: :destroy
def serialize(options={})
UserSerializer.new(options).serialize(self)
end
end
or you can simply create module for reuse.
module Serializable
def serialize(options={})
serializer = options[:serializer] || serializer_class_name
serializer.new(options).serialize(self)
end
private
def serializer_class_name
"#{self.class.name}Serializer".constantize
rescue NameError => e
raise "Missing #{self.class.name} serializer."
end
end
@irondnb thanks for that helpful example! I think it can certainly be done in the userland but I think it feels weird that it is inconsistent with how other has_one
and has_many
relations work automagically within a serializer and polymorphic ones don't seem to be. If the inference here works, then they will end up behaving consistently with other relations which would be amazing :)