47

Let's say you're implementing a REST API in Rails. When serving a collection, you might want to only include a few attributes:

/people

But when serving a single resource, you want to include all the attributes:

/people/1

I don't see how to do that using ActiveModel::Serializers, since the examples all use the pattern of defining one serializer per model (with a standard naming convention) and having AMS automatically use the right one in the controller when you do:

render json: @people

or:

render json: @person
odigity
  • 7,568
  • 4
  • 37
  • 51

5 Answers5

130

You can have multiple serializers for the same model, e.g.

class SimplePersonSerializer < ActiveModel::Serializer
  attributes :id, :name
end

and

class CompletePersonSerializer < ActiveModel::Serializer
  attributes :id, :name, :phone, :email
end

simple info for people in one controller:

render json: @people, each_serializer: SimplePersonSerializer

complete info for people in another:

render json: @people, each_serializer: CompletePersonSerializer

simple info for a single person:

render json: @person, serializer: SimplePersonSerializer

complete info for a single person:

render json: @person, serializer: CompletePersonSerializer
phaedryx
  • 2,024
  • 3
  • 16
  • 24
11
class CompletePersonSerializer < ActiveModel::Serializer
  root :person
  attributes :id, :name, :phone, :email
end

or

render json: @people, each_serializer: CompletePersonSerializer, root: :person
4

To avoid mixing view concerns into your models (via serialized variations), use the view to render your JSON for each action, much like we do for HTML.

jbuilder & rabl both fill this data templating need quite nicely.

Update 2013-12-16: The ActiveModelSerializers library does support defining multiple serializers for one model, as @phaedryx answered later, by using custom serializers.

Mars
  • 1,530
  • 10
  • 15
  • 2
    So, what you're saying is that AMS doesn't yet have the flexibility in it's current design to satisfy this common use case of differently serializing for different API responses? Because that's how it feels, but I didn't want to assume the problem wasn't simply my own ignorance, especially since AMS is being considered for Rails4 core as "the way"... – odigity Sep 27 '12 at 12:50
  • 1
    jbuilder was created by DHH specifically to address this problem in Rails. Creating variations via ActiveModel::Serializers will end up putting public-API (view) concerns in your controllers &/or models. – Mars Sep 28 '12 at 00:26
  • Ok. Obviously I need to look at jbuilder and then decide how to proceed, but it was a good answer. Thanks! – odigity Sep 28 '12 at 06:29
2

Adding to what @phaedryx said, what I do for this is call a method that returns the correct serializer... for your question, I'd use:

class MyController < ApplicationController

  def index
    render json: @people, each_serializer: serializer_method
  end

  private

  def serializer_method
    defined?(@people) ? PeopleSerializer : PersonSerializer
  end

end
tehprofessor
  • 2,957
  • 1
  • 21
  • 22
0

IMO it's best to have a specific serializer for each controller action. I built this concern to handle it. (which I add to base controller)

module Serializable
  extend ActiveSupport::Concern

   alias each_serializer serializer

   def serializer
     "#{params[:controller].classify}s::#{params[:action].classify}Serializer".constantize
   end
end

That way you can just call

render json: @person, serializer:

in your controller and it will find the correct serializer.

heratyian
  • 414
  • 3
  • 13