When using group_by
in a controller that serializes the grouped collection to JSON you will notice that your serializers created with the active_model_serializer
will not be used and all attributes in your model are just converted to JSON. For example:
class SomeModel
#attributes: :name, :body, :some_group_key
end
class SomeModelSerializer
attributes :name
end
# in controller
def index
@some_models = SomeModel.all.group_by(&:some_group_key)
render json: @some_models, each_serializer: SomeModelSerializer
end
So this will result in something like this:
{
"GROUP_KEY_VALUE" => [
{ "name" => ..., "body" => ..., "some_group_key" => "GROUP_KEY_VALUE", "created_at" => ... ...},
{ "name" => ..., "body" => ..., "some_group_key" => "GROUP_KEY_VALUE", "created_at" => ... ...}
],
"GROUP_KEY_VALUE2" => [
{ "name" => ..., "body" => ..., "some_group_key" => "GROUP_KEY_VALUE2", "created_at" => ... ...},
...
]
}
So even when an explicit serializer is given, it will be ignored since the serializer will find a hash instead of the collection and all attributes will be rendered out.
To change this, so that the serializer will be used, write a custom serializer:
class GroupedSomeModelSerializer < ActiveModel::Serializer
# method override
def serializable_hash
@object.map do |some_group_key, some_models|
[ some_group_key , serialized_some_models(some_models) ]
end.to_h
end
private
def serialized_some_models some_models
some_models.map{ |some_model| SomeModelSerializer.new(some_model, root: false) }
end
end
And then in the controller you just need call your serializer as an each serializer:
# in controller
def index
@some_models = SomeModel.all.group_by(&:some_group_key)
render json: @some_models, serializer: GroupedSomeModelSerializer
end
Then the result should be grouped and use your serializer:
{
"GROUP_KEY_VALUE" => [
{ "name" => ...},
{ "name" => ...}
],
"GROUP_KEY_VALUE2" => [
{ "name" => ...},
...
]
}
active_model_serializers > 0.9
For the new versions of AMS you need to override serializeable_object
instead of serializable_hash
:
def serializable_object(options={})
@object.map do |some_group_key, some_models|
[ some_group_key , serialized_some_models(some_models) ]
end.to_h
end