Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make an ActiveModel::Serializer attribute optional at runtime?

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?

like image 441
Nathan Hanna Avatar asked Sep 09 '15 18:09

Nathan Hanna


3 Answers

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.

like image 65
robsonmwoc Avatar answered Sep 19 '22 22:09

robsonmwoc


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 )
like image 24
voiski Avatar answered Sep 19 '22 22:09

voiski


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
like image 38
droptheplot Avatar answered Sep 21 '22 22:09

droptheplot