Use ActiveModel::Serializers to include two parent json arrays
Asked Answered
T

2

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!

Tessellated answered 11/5, 2013 at 21:40 Comment(0)
T
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 
}}
Tessellated answered 12/5, 2013 at 1:23 Comment(2)
This is exactly the same problem I had. Thanks for a great answer!Knitter
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.Geosynclinal
S
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.

Stepp answered 2/10, 2014 at 16:12 Comment(4)
Since a recent update to active_model_serializers this solution is no longer valid. Application should make two separate requests.Stepp
I'm just curious, per your comment, why this is no longer valid? I'm using the latest version and everything runs smoothly.Fieldfare
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.Stepp
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.Stepp

© 2022 - 2024 — McMap. All rights reserved.