Rails: Using a custom serializer to serialize a collection of active record model instances
Published: 2020-03-18 02:39:31

So, serializers provide a standardized way to shape and characterize the data an endpoint needs to return based on a given response. There are a number of ruby gems that do serialization. One popular gem is 'active_model_serializers'.

If you have the active_model_serializers gem enabled in your rails app, and your endpoint renders some json, such as: 'render json: iAmAnActiveRecordBookInstance' -- well if you have a serializer in the serializers folder with the same name as the model instance, rails will make use of it, and provide the serialized data of your book, based on that serializer.

However, sometimes we may want to include or exclude information for a particular model instance based on relationships to other models. And, in this situation, it's likely we're going to have custom serializers that we wish to employ in the given endpoint. There is a simple way to do this:

render json: myModel, serializer: mySerializer

In this way we can utilize a custom serializer

However, often we have a list of active record associations we wish to serialize. If we just wish to let rails employ the default serializer action, then we can do something like this:  render json: myModelInstance.all   

In the above case, rails will look in the serializers folder for a serializer with the same name as the model instance, and apply it to all the items in the active record 'list'. This is fine if what you want are all your instances to serialize via the default serializer for the model. However, sometimes we don't want that: we want a custom serializer.

If you try this, you will run into an error: 'render json: Book.all, serializer: customBookSerializer'

So, why does this fail, just because we specify the serializer? A collection of instances is not an instance: A serializer for a collection of instances is not a serializer for an instance. When we left it to rails' default behavior, there is a check for whether this is just an instance of a model, or a collection of model instances, and rails would employ a collection serializer and the collection's instance serializer. But if we specify our serializer, we are taking control of the serialization process, and we need to be a little bit more specific if we want our custom serializer to apply to a collection of objects of a given kind.

So, here's how you make use of a custom serializer for a collection of active record model instances: render json: Book.all, serialize_each: customSerializer

Sometimes, your serializers may need information in order to serialize the information you need. You can pass in information, such as a userId or an itemId in this way:

'render json: Book.all, serialize_each: customSerializer, additionalProperty: additionalPropertyValue
 
Inside of your serializer, you can access additionalProperty as 'instance_options[:additionalProperty]'.   

You might wonder, "why would I ever need to pass information to my serializer, if the relationships are set up correctly?" You might not need to. But one very useful case is where you have a many-to-many relationship with another model, via a join table, and you need to retrieve certain records from that join table, based on dynamic information. You can make a custom method in the serializer, and grab the relevant records via the dynamic information you pass in via instance options.