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?
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?
317004058a610c28ac15cbeed459ceb2c9323da6
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...
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).
:+1: having the same issue
do any maintainers have an opinion on this? is this feature going to be accepted if we build it?
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?
@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.
@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?
yup, I can do as many chains as possible with JSON API as long as the associations are declared in the serializer classes
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?
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?
Hey ppl, first of all thank you all for helping each other :smile:
First things first, nested associations will be a feature on 0.10.x, a lot of ppl have being requesting it. :tada:
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. :smile:
btw @piotrpalek, you should give a try to JSON-API, awesome convention, and we really support and advise it.
@shicholas thanks for the example I will try it later :)
@joaomdmoura yeah I will give it a try thx :)
Any updates on this?
@vyrak Well this worked for me (from schicholas example): include: ['hand.fingers'] to include the nested fingers association.
@piotrpalek That's only when using the JsonAPI adapter, right?
@yjukaku yeah right, I think it's only jsonapi adapter (didn't try any other)
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.
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!
@eric-norcross https://github.com/rails-api/active_model_serializers/issues/835 :smile:
@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.
@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 https://github.com/rails-api/active_model_serializers/issues/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?!
@shicholas thanks for the quick response. It is finally working now. Thanks for your code because it helped me. Cheers ):
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.
@sagits It is not possible as it is not part of the JSON API spec AFAIK.
@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?
Sure @bf4, here it is 1540.
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']
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.
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.
@fazo96 this might help? https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/general/adapters.md#include-option
fyki, include is an adapter option
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.
@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
Most helpful comment
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.
This should give serialized bodies with both hands and fingers, is this what you had in mind @piotrpalek?