Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to include nested associations? #968

Closed
piotrpalek opened this issue Jun 25, 2015 · 32 comments
Closed

How to include nested associations? #968

piotrpalek opened this issue Jun 25, 2015 · 32 comments

Comments

@piotrpalek
Copy link

Hey, let's say I have a Post model which has_many :comments. And I use it like this in the post index controller: render json: post, include: ['comments']. That works fine and includes the comments, but let's say the Comment model belongs_to :user now. Writing render json: post, include: ['comments', 'user'] doesn't "sideload" the data, is there a solution to this?

@doooks
Copy link

doooks commented Jun 25, 2015

Hi @piotrpalek , I ran into this problem just as you posted this. I've forked and committed a failing test that shows the behaviour I was trying to produce

Does this look like the same problem to you?

3170040

@dimonzozo
Copy link

Hi @piotrpalek, @doooks. I have the same issue right now.

And I think this is design decision and if adapter will render has_many relations recursively we'll get loop. Post -> Comments -> Post -> Comments...

@piotrpalek
Copy link
Author

My problem is with AMS 0.10-rc2 and the json-api adapter (just checking if we're talking about the same issue) and I don't know how to sideload nested relationships with that setup (or if it's possible at all).
@doooks My issue is similiar but a bit different, in my example the comment wouldn't have an author (just the post), and I would try to sideload the post's author while serializing the comment (with sideloaded post relationship).

@shicholas
Copy link
Contributor

👍 having the same issue

@doooks
Copy link

doooks commented Jun 29, 2015

do any maintainers have an opinion on this? is this feature going to be accepted if we build it?

@shicholas
Copy link
Contributor

Fwiw I ended up switching to the JSON API standard. I think its handling of associated records is a great alternative to nesting json. What are your reservations to the json API spec?

@doooks
Copy link

doooks commented Jun 29, 2015

@shicholas I'd never heard of it until recently, sure its great but I would rather have json that represents the model a bit more intuitively.

@piotrpalek
Copy link
Author

@shicholas Are you able to "sideload" a nested relationship using JSON API? What I mean is:
Blog -> belongsTo -> Author
Comment -> belongsTo -> Blog

And now what I want to do is load all comments and sideload all the corresponding blogs but also sideload all relevant blog authors, is that possible?

@shicholas
Copy link
Contributor

yup, I can do as many chains as possible with JSON API as long as the associations are declared in the serializer classes

@piotrpalek
Copy link
Author

To include the associations you got to declare them in the controller via include: ['something'] right? Can you give an example how to include the nested associations?

@shicholas
Copy link
Contributor

sure here's what I did, my example: a body has one hand which has many fingers. This should serialize a collection of bodies including hands and fingers.

class BodySerializer < ActiveModel::Serializer
    attributes(:name)

    has_one :hand

    url :body
end
class HandSerializer < ActiveModel::Serializer
    attributes(:wrist)

    belongs_to :body
    has_many :fingers

    url [:body, :hand]
end
  class FingerSerializer < ActiveModel::Serializer
    attributes(:knuckle)

    belongs_to :hand

    url [:body, :hand, :fingers]
  end
  class V1::BodyController < ApplicationController
    def index
      bodies = Body.all
      render json: bodies, include: 'hand,fingers'
    end
  end

This should give serialized bodies with both hands and fingers, is this what you had in mind @piotrpalek?

@joaomdmoura
Copy link
Member

Hey ppl, first of all thank you all for helping each other 😄
First things first, nested associations will be a feature on 0.10.x, a lot of ppl have being requesting it. 🎉

It started back at #835, there was a PR (#952) I reviewed it a while ago, but it's broken, I'll probably pick it up in some days.
Let me know if some of you want to pick it first of work on a new implementation, I would like it to be adapter agnostic (I know JSON API is a case apart), if possible, well just let me know and we can talk it trough, otherwise I'll work on it asap, but I still need to finish a new feature that we are working on. 😄

btw @piotrpalek, you should give a try to JSON-API, awesome convention, and we really support and advise it.

@piotrpalek
Copy link
Author

@shicholas thanks for the example I will try it later :)
@joaomdmoura yeah I will give it a try thx :)

@vyrak
Copy link
Contributor

vyrak commented Jul 21, 2015

Any updates on this?

@piotrpalek
Copy link
Author

@vyrak Well this worked for me (from schicholas example): include: ['hand.fingers'] to include the nested fingers association.

@yjukaku
Copy link

yjukaku commented Jul 22, 2015

@piotrpalek That's only when using the JsonAPI adapter, right?

@piotrpalek
Copy link
Author

@yjukaku yeah right, I think it's only jsonapi adapter (didn't try any other)

@joaomdmoura
Copy link
Member

Taking into consideration there is a issue and some work in progress on a PR related to it, I'm closing this one in favor of others.

@eric-norcross
Copy link

I know this has been closed, but could anyone point me to the most recent/appropriate issue for including nested associations?

I would love to see this feature included sooner rather than later!

@joaomdmoura
Copy link
Member

@eric-norcross #835 😄

@mankind
Copy link

mankind commented Aug 27, 2015

@shicholas and @piotrpalek the approach that worked for two of your in creating nested association using json-api with include is not working for me. I am using 0.10.0.rc2 and I tried making nested association with include but it is not working.

   class UserSerializer < ActiveModel::Serializer
        attributes :id, :email
        has_one :account
   end

   class AccountSerializer < ActiveModel::Serializer
        attributes :id, :name, :domain
        has_many :segments
   end

   class SegmentSerializer < ActiveModel::Serializer
        attributes :id, :name,  :active
        has_many :base_segments
        has_one :account
    end

    class  Api::UsersController < ApplicationController
        def index
            @users = User.all
            render json: @users, include:  ['account', 'account.segments'] 
        end
    end

I also tried render json: @users, include: ['account', 'segments']
Is there I anything I am missing to get this working. Thanks for your help.

@shicholas
Copy link
Contributor

@mankind, first of all, excellent name :).

Your serializer definitions look good to me. What type of error are you getting?

FWIW, I am using a concern for JSON API rendering because I am trying to use both .8 and .10 in the same app. My controller concern looks like:

module JsonApiHelpers
  extend ActiveSupport::Concern

  private

  def render_serialized_array(model_array)
    serializer_instance = serializer_for(model_array).new(
      model_array,
      each_serializer: serializer_for(model_array)
    )

    render json: adapter(serializer_instance)
  end

  def render_serialized_model(model)
    serializer_instance = serializer_for(model).new(model)

    render json: adapter(serializer_instance), status: :ok
  end

  def serializer_for(model)
    @_serializer ||= ActiveModel::Serializer.serializer_for(model)
  end

  def included; [] end # rubocop:disable Style/SingleLineMethods

  def adapter_opts
    { adapter: 'json_api', include: included }
  end

  def adapter(serializer_instance)
    ActiveModel::Serializer::Adapter.create(
      serializer_instance,
      adapter_opts
    )
  end
end

which I learned from #1002. Then in my controller I include the concern, override included and call render_serialized_array for collections or render_serialized_model for a single resource.

maybe that helps?!

@mankind
Copy link

mankind commented Aug 27, 2015

@shicholas thanks for the quick response. It is finally working now. Thanks for your code because it helped me. Cheers ):

@sagits
Copy link

sagits commented Feb 25, 2016

How can i implement this on Post and Put requests? I mean, instead of passing an id, i would like to pass the entire model relation to my POST (user : {name : "sagits", category : {id : 10}}. Is this possible? Thanks in advance.

@beauby
Copy link
Contributor

beauby commented Feb 25, 2016

@sagits It is not possible as it is not part of the JSON API spec AFAIK.

@bf4
Copy link
Member

bf4 commented Feb 26, 2016

@sagits Could you create a new issue please, including your AMS version, Rails version, and some code showing what you're trying to do and what you expect?

@sagits
Copy link

sagits commented Feb 26, 2016

Sure @bf4, here it is 1540.

@rromanchuk
Copy link

For anyone reading this who is using the :json adapter, and having issues with rendering associations, one of the documented methods, for whatever reason, does not work. You must define your includes as include: ['foo', 'bar']

51af5a4#commitcomment-21425180

I have not spent the time yet to find the offending change that broke this, but hopefully I might save some hair puling. This would effect anyone inheriting an old project running a bundle update on an unlocked project.

@fazo96
Copy link

fazo96 commented Nov 22, 2019

In case anyone like me was using a serializer manually like this: MySerializer.new(record) and could not figure out how to pass include: it's done like this: MySerializer.new(record).to_h(include: '**') because passing this parameter to the constructor does not work.

@wasifhossain
Copy link
Member

wasifhossain commented Nov 22, 2019

@fazo96
Copy link

fazo96 commented Nov 22, 2019

Yes, we use it like that most of the time. However sometimes we need to include a serialized record inside a bigger hash structure that does not come from another active model serializer.

In those cases the include option has to be passed when calling to_hash or one of its aliases. I only figured this out by looking at the source code and could not find any documentation on this.

@wasifhossain
Copy link
Member

@fazo96 i see. i think its also documented on how to serialize a resource outside of a controller: docs/howto/outside_controller_use.md#serializing-a-resource

so you can pass every option to the ActiveModelSerializers::SerializableResource initializer that you would pass in a render call.

for your case, it would be like:

ActiveModelSerializers::SerializableResource.new(record, include: '**').as_json

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests