13

I'm trying to send my front-end application json that looks like this:

{
  facilities: [
     {id: 5, name: 'happy days ranch', location: { address: '1424 Pastoral Lane', zipcode: '25245'}, instructor_ids: [2, 4, 9]}
  ],
  instructors: [
     {id: 4, name: 'Johnny Pheonix', skill: '8', picture: 'aws_url', facility_ids: [5, 8, 12}
  ]
}

Things I have tried

render :json => @facilities 

The serializer discovers this. Yay! But this does not include any instructors

render :json => {facilities: @facilities, instructors: @instructors}

This gives me an instructors array and a facilities array, but activeModel::Serializers is not used.

render :json => [@facilities, @instructors]

At first I was excited about this one, because it gave me two arrays, and it used ActiveModel::Serializers. However, this is what the JSON looked like:

{facilities: [
  {facilities: [
    #my facilities data
  ]},
  {facilities: [
    #my instructor data
  ]}
]}

Is what I'm trying to do even allowed by ActiveModel::Serializers? If so, how?

Thanks much in advance!

Jeffrey Biles
  • 988
  • 7
  • 24

2 Answers2

20

I solved it by creating a class called Search that incorporates aspects of ActiveModel

class Search
  include ActiveModel::Serialization
  include ActiveModel::SerializerSupport

  attr_accessor :facilities, :instructors

  def initialize(facilities, instructors)
    @facilities, @instructors = facilities, instructors
  end
end

Then I created a Searches controller (nothing interesting there) and a Search serializer.

class SearchSerializer < ActiveModel::Serializer
  has_many :instructors, embed: :objects
  has_many :facilities, embed: :objects
end

This creates my desired json, although now it is wrapped in a search hash:

{search: {
  #the stuff I wanted 
}}
Jeffrey Biles
  • 988
  • 7
  • 24
  • This is exactly the same problem I had. Thanks for a great answer! – Erich Douglass Jul 20 '13 at 20:44
  • 3
    Thank you for your solution. It works, and you can disable the root node 'search' by setting `root: false` in the renderer. After review, we decided not use this approach with 2 root elements, We ended up going with 2 separate API endpoints, which keeps our design simpler. – Michael Mar 05 '14 at 18:22
7

This is my solution:

render json: {
  facilities: ActiveModel::ArraySerializer.new(@facilities, each_serializer: FacilitySerializer, root: false),
  instructors: ActiveModel::ArraySerializer.new(@instructors, each_serializer: InstructorSerializer, root: false)
}

It's a little bit dirty. It basically instantiates what would be instantiated except done manually and twice. Both result sets are rendered using ActiveModel::Serializers in the correct format.

kequc
  • 467
  • 6
  • 10
  • 1
    Since a recent update to active_model_serializers this solution is no longer valid. Application should make two separate requests. – kequc Nov 13 '14 at 15:19
  • I'm just curious, per your comment, why this is no longer valid? I'm using the latest version and everything runs smoothly. – David Nov 20 '15 at 18:43
  • I've learned since posting this solution why it's so important to provide a lot of additional information in the form of links to sources. Unfortunately I cannot decipher what I was talking about at the time, same as you. I apologise for not having been more verbose and will start to be more so in the future. I think it has to do with how activemodel serialisers expects output to look, and incompatibility with some external libraries. If all you need is raw output this solution may still be valid. – kequc Nov 23 '15 at 18:27
  • I find it's better not to fight convention in general. So my inclination if a library doesn't natively offer a way to return two result sets at once, why not perform two requests. – kequc Nov 23 '15 at 18:32