20

I'm trying to build a JSON representation of some Rails models using Active Model Serializer, where some models embed others. For example, I have Event and Attendees, Event has_and_belongs_to_many Attendees.

class EventSerializer < ActiveModel::Serializer
  attributes :name

  has_many :attendees, serializer: AttendeeSerializer
end

class AttendeeSerializer < ActiveModel::Serializer
  attributes :name
end

This would result in JSON like { name: 'Event One', attendees: [{ name: 'Alice' }, { name: 'Bob' }] }.

Now, I'd like to add what the attendees have said about the event. Let's say, Comment belongs_to Event, belongs_to Attendee. I'd like to include said comments in the serialized output of event, so it would become { name: 'Event One', attendees: [{ name: 'Alice', comments: [{ text: 'Event One was great!'}] }, { name: 'Bob', comments: [] }] }.

I could have

class AttendeeSerializer < ActiveModel::Serializer
  attributes :name

  has_many :comments
end

but that would select all the comments by this attendee for all the events - not what I want. I'd like to write this, but how do I find the particular event for which I'm doing serialization? Can I somehow access the 'parent' object, or maybe pass options to a has_many serializer?

class AttendeeSerializer < ActiveModel::Serializer
  attributes :name

  has_many :comments

  def comments
    object.comments.where(event_id: the_event_in_this_context.id)
  end
end

Is this something that can be done, or should I just build the JSON in another way for this particular use case?

Toms Mikoss
  • 9,097
  • 10
  • 29
  • 41

1 Answers1

56

I'd do things manually to get control:

class EventSerializer < ActiveModel::Serializer
  attributes :name, :attendees

  def attendees
    object.attendees.map do |attendee|
      AttendeeSerializer.new(attendee, scope: scope, root: false, event: object)
    end
  end
end

class AttendeeSerializer < ActiveModel::Serializer
  attributes :name, :comments

  def comments
    object.comments.where(event_id: @options[:event].id).map do |comment|
      CommentSerializer.new(comment, scope: scope, root: false)
    end
  end
end
apneadiving
  • 114,565
  • 26
  • 219
  • 213
  • 1
    Yeah, this works and isn't that bad. Guess I was too fixated on making it happen via 'has_many' serializer option :) – Toms Mikoss Jul 29 '14 at 09:38
  • 2
    This answer has been very help for me as well! I do have one issue though. I am doing something very similar as the OP but I'm embedding my associations with `embed: :ids, include: true` in all my serializers. When I create the array of serializers manually, these serializers do not have embedded ids. It seems like the root cause is changing the child serializer from an association to an attribute. I've tried naive things like adding `embed: :ids` to the initializer without success. Any guidance would be appreciated :) – xph Sep 23 '14 at 06:55
  • @significance no, not the issue stated in my last comment. I ended up using a workaround that restructured the relationship between my models. – xph Jan 22 '15 at 21:02
  • @JaredMenard yes totally, I tend to present all options (scope is a very important one) to give a full grasp of the tool – apneadiving Sep 13 '15 at 11:36
  • @apneadiving Can you please help me to solve similar kind of issue statted here, https://stackoverflow.com/questions/44201299/pass-rails-methodassociation-to-react-js – Anish May 27 '17 at 11:20
  • `has_many` takes a `&block` which can be use to do all these filtering required. – skam Aug 17 '17 at 11:53