active_model_serializers icon indicating copy to clipboard operation
active_model_serializers copied to clipboard

0.10.x: associations are not included when nested

Open patrick99e99 opened this issue 7 years ago • 8 comments

Expected behavior vs actual behavior

If I have an object: cat = Cat.find_by_name('fluffy')

and assume that I can do: cat.paws.last.claws.count => 5 cat.paws.first.claws.count => 4

...

If my controller renders out the cat with a nested serializer structure such as: respond_with cat, :serializer => CatSerializer

...

class ClawSerializer < ActiveModel::Serializer
   attribute :sharpness
end

class PawSerializer < ActiveModel::Serializer
  attribute :position
  has_many :claws, :serializer => ClawSerializer
end

class CatSerializer < ActiveModel::Serializer
  attribute :name
  has_many :paws, :serializer => PawSerializer
end

...

I get the json structure:

{
    name: 'fluffy',
    paws: [
      { position: 0 },
      { position: 1 },
      { position: 2 },
      { position: 3 },
    ]
}   

With no claws...

Yet again, if I explicitly do:

class PawSerializer < ActiveModel::Serializer
  attribute :claws
  attribute :position

  def claws
     object.claws.map do |claw| 
       ClawSerializer.new(claw)
     end
  end
end

Then I get the structure I expect:

{
    name: 'fluffy',
    paws: [
      { position: 0, claws: [ { sharp: true }, { sharp: true }, { sharp: true }, { sharp: true }, { sharp: true } ] },
      { position: 1, claws: [ { sharp: true }, { sharp: true }, { sharp: true }, { sharp: true }, { sharp: true } ] },
      { position: 2, claws: [ { sharp: false }, { sharp: false }, { sharp: false }, { sharp: false }, { sharp: false } ] },
      { position: 3, claws: [ { sharp: false }, { sharp: false }, { sharp: false }, { sharp: false }, { sharp: false } ] },]
     ]
}

patrick99e99 avatar Apr 13 '17 00:04 patrick99e99

Have you tried using the include option in your controller? I believe you have to explicitly state the associations you wish to include: https://github.com/rails-api/active_model_serializers/blob/master/docs/general/adapters.md#include-option

cassidycodes avatar Apr 13 '17 15:04 cassidycodes

You can also see an example of this in the tests: https://github.com/rails-api/active_model_serializers/blob/master/test/action_controller/json/include_test.rb#L43

cassidycodes avatar Apr 13 '17 15:04 cassidycodes

I thought that was only if you are using the json api adapter? Because I am not using that adapter...

patrick99e99 avatar Apr 13 '17 15:04 patrick99e99

@patrick99e99 I believe it's for the JSON and JSON API adapter, the docs are a little unclear there. I haven't tested it with the attributes adapter myself.

cassidycodes avatar Apr 13 '17 16:04 cassidycodes

Just spent some time trying to get my head around this too, as I have a really similar problem to @patrick99e99 but using a has_one instead of has_many and ended up with a similar solution too... as in calling another serializer from a serializer to get the output I wanted as the nested association didn't work as I thought it would (or should).

So is it the case that nested associations should work and don't or has it never been the case and people should alway put include in their controllers?

Also, and this might be me being dumb, but shouldn't creating serializers mean we shouldn't have to continue to use include in our controllers? I guess what I mean is shouldn't there be a way to specify the includes in the serializers?

BTW it did work as expected if I put the include in the controller.

schinery avatar May 08 '17 16:05 schinery

@patrick99e99 From https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/configuration_options.md

default_includes What relationships to serialize by default. Default: '*', which includes one level of related objects. >See includes for more info.

To serialize all associations by default set ActiveModelSerializers.config.default_includes = '**'

LeKristapino avatar Jun 27 '17 14:06 LeKristapino

how about this? I use it to fix has_one problem about not included in nested case.

attirbute :foo do 
  FooSerializer.new object.foo
end 

cactis avatar Apr 03 '19 05:04 cactis

@cactis the standard way AMS suggests is to define the has_one association using the same keyword in the serializer:

# some_serializer.rb

has_one :foo

and in the controller, render it along with the include option putting a value of ** in it if the parent of this association (the object of the above serializer) is at least 1-level deep from the controller resource.

Doc

wasifhossain avatar Apr 03 '19 07:04 wasifhossain