Active_model_serializers: Response with multiple lists

Created on 28 Aug 2015  路  10Comments  路  Source: rails-api/active_model_serializers

Hi,

is there a way to specify something like this:

render json: {projects: @projects, each_serializer: ProjectSerializer, clients: clients, each_serializer: ProjectSerializer}, status: :ok

Most helpful comment

You could try something like this.

render json: {
        projects: ActiveModelSerializers::SerializableResource.new(projects, each_serializer: ProjectSerializer),
        clients: ActiveModelSerializers::SerializableResource.new(clients, each_serializer: ClientSerializer),
  }, status: :ok

All 10 comments

This is not currently possible. Two workarounds:

  1. make a model that encapsulates the two collections (something like ProjectsAndClients), and serialize that (being careful of the few gotchas of serializing POROs)
  2. make an heterogenous array with all your projects and clients, and serialize that (the array serializer will select the right serializer for each object), and split it into two arrays on the client side

Edit: for 1. you could actually just define a serializer, and specify it manually in your render call.

Thanks, for the second option, if i have multiple serializers for projects and clients, how can i specify the right one?

Not sure this is possible under the current circumstances. @joaomdmoura @bf4?
If I were you, I would go with 1. and do something along the lines of:

class ProjectsAndClientsSerializer < ActiveModel::Serializer
  has_many :projects, serializer: MyFancyProjectSerializer
  has_many :clients, serializer: MyFancyClientSerializer
end

or build a custom CollectionSerializer, if you want to stick with 2.

If you decide to stick with 2. you could modify ArraySerializer to make it suit your needs (that is requiring a specific adapter for each record depending on whether it is a client or a project).

if i go with the first option, do i need to create an ActiveRecord?
if i use render json: {projects: @projects, clients: clients}, serializer: ClientsAndProjectsSerializer i get error "undefined method projects for Hash<..."

No, you do not need ActiveRecord for 1., although there are currently a few gotchas for serializing PORO models (search in the issues, you should define some methods like read_attribute_for_serialization(attr).
You should encapsulate it in a Model like:

class ProjectsAndClients < ActiveModel::Model
  attr_accessor :projects, :clients
  def initialize(projects, clients)
    @projects = projects
    @clients = clients
  end
end

then do

render json: ProjectsAndClients.new(projects, clients), serializer: ClientsAndProjectsSerializer

Edit: in that case you probably don't need the read_attribute_for_serialization method, and it should pretty much work out of the box.

oh, great! Thanks a lot man!

I'm not at a computer now so I can't be as precise as I'd like.

  1. Since this issue is closed, would you mind sharing your solution including gem versions
  2. Just reading the github emails Ot doesn't seem like it would work

It sounds like either you'd like to treat a specific collection as a single resource
Or you'd like to customize an adapter for a specific collection

If the former, this should be in the docs, and it's certainly in the lint but

class PostOffice
include ActiveModel::Model
include ActiveModel::Serialization
attr_accessor :post, :office
end
class PostOfficeSerializer < AM::S
has_one :post, serializer: PostSerializer
has_one :office, serializer: OfficeSerializer
end
render json: PostOffice.new(post: post, office: office
(No need I don't think to specify each_serializer: PostOfficeSerializer

The idiomatic way to use a custom collection serializer would be (in 0.10)

render json: [post, office], serializer: PostOfficeSerializer
Where
class PostOfficeSerializer < AM::ArraySerializer
def serializable_hash(options = nil)
result = super
# something, maybe even don't call super
end
end

Or maybe an adapter

class SpecialAdapter < AM::S::Adapter::Json
# something
end
AM::S.config.adapter = SpecialAdapter
If you want it as the default
Otherwise you'd need to namespace it like the other adapters (for now) and pass in adapter: special

Hope that helps

Me on mah phone and achy fingers

Whatever works plz help us improve the docs if you can and submit a PR!

You could try something like this.

render json: {
        projects: ActiveModelSerializers::SerializableResource.new(projects, each_serializer: ProjectSerializer),
        clients: ActiveModelSerializers::SerializableResource.new(clients, each_serializer: ClientSerializer),
  }, status: :ok

Used this, then the scope is present in the serializer like current_user:

foo = OpenStruct.new(projects: projects, clients: clients)
render json: foo, adapter: :attributes, serializer: ProjectsClientsSerializer

...

class ProjectsClientsSerializer < ActiveModel::Serializer
    type ''
    has_many :projects
    has_many :clients

    def read_attribute_for_serialization(attr)
      object[attr.to_s]
    end
  end
Was this page helpful?
0 / 5 - 0 ratings