7

I am trying to allow an API request to specify what fields to return on an object. I can retrieve the object with only the fields specified, but when it is serialized, it throws an error:

ActiveModel::MissingAttributeError (missing attribute: x)

How can I achieve this functionality with ActiveModel::Serializer and is it possible?

dimakura
  • 7,575
  • 17
  • 36
Nathan Hanna
  • 4,643
  • 3
  • 28
  • 32

4 Answers4

6

I've found this question while searching for a good alternative to remove optional fields from the json response.

The gem active_model_serializers does have a solution for this. You just need to pass a conditional to the attribute method in the serializer declaration.

class MySelectiveSerializer < ActiveModel::Serializer
  attributes :id, :anything
  attribute :something, if: -> { object.something.present? }
end

Perhaps 3 years ago a solution like this didn't exist, but it is available now. :)

Cheers.

wscourge
  • 10,657
  • 14
  • 59
  • 80
robsonmwoc
  • 181
  • 3
  • 8
3

This happens because the Serializer.attributes method call each field using the ActiveModel.read_attribute method. This method will apply some validations, like validates_presence_of at the model's definition, that will raise the exception. To avoid it I give three bad solutions and after a better and simple one:

  • Change the model definition, but you will miss your validation.
  • Overwrite the method ActiveModel.read_attribute to handle this behavior, you will get new challenges.
  • Overwrite the Serializer.attributes and instead of call super, call object.attributes.

But the best option will be create a new serialize class, to avoid besides effects, with the only fields that you want. Then specify this at the controller class:

render json: People.all.reduced, each_serializer: SimplePersonSerializer

Edit 1

The right answer should be the one from Maurício Linhares.

render json: result.to_json( only: array_of_fields )
voiski
  • 437
  • 5
  • 10
  • The final suggestions doesn't meet the challenge. OP wants the API consumer to be able to specify any combination of fields rather than a specific, predefined subset. – Adamantish Nov 16 '17 at 13:50
  • @Adamantish thanks for this, I didn't realize that it should be flexible, my bad =( – voiski Nov 17 '17 at 14:43
2

You can remove attributes from serializer, but they should exist.

class SomeSerializer < ActiveModel::Serializer
  attributes :something

  def attributes
     super.except(:something) if something
   end
end
droptheplot
  • 608
  • 6
  • 22
0

You can customize attributes by implementing filter method in your serializer. Note, that I describe latest stable (for the time of the writing this post) 0.9.x branch.

class PostSerializer < ActiveModel::Serializer
  attributes :id, :title, :body, :author

  def filter(keys)
    if scope.admin?
      keys
    else
      keys - [:author]
    end
  end
end
Alexey Shein
  • 7,342
  • 1
  • 25
  • 38
  • Doesn't solve the problem filter is called after the looking for all the attributes so error is still thrown. – Adamantish Nov 16 '17 at 13:47