-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[RFC]Thoughts on an explicit way to define nested relationships? #1113
Comments
Hi @NullVoxPopuli, just to answer your last question (I'm the author of #952) the nesting can actually bring the recursive problem, but that's intended. It is actually a very similar feature to your side-loading, as you're actually asking to AMS to side-load all associations without explicitly declaring them. That's allow you to keep your code easier to maintain (if you add a new relationship later, that will be taken automatically).
This can actually fire the recursive problem, actually all many-to-many associations share this risk. But again, that's intended, because it depends on how you use the include_nested_associations option.
As I said before, you can always make even better defining different serialisers based on your needs. |
thanks for explaining your your technique. But yeah, I'm trying out this solution to the recursive problem: #1114 CommentSerializer < #...
belongs_to :author
end
PostSerializer < #...
has_many :comments, include: :author
end
BlogSerializer < #...
has_many :posts, include: [:comments]
end when rendering a I think this is handy, in that you have a more explicit control of how deep the recursion goes |
Yes, that definitely makes sense, and if you want the author too you just need to change the BlogSerializer like this:
Recursive loop problem should be fixed as well, the problem is that if you want a standard behaviour (like you always want the comment author when you get a comment), you have to copy-and-paste the same "include" option in all serialisers, where with the include_nested_associations you delegate this to the serializer itself and you just need to manage special cases. |
Yeah, I don't think either should conflict with each other (functionality-wise anyway, I'm sure there are code conflicts, as I refactored a bit too) |
@iMacTia so, lets say someone wants to always include the author on their comments, how do you feel about the following? CommentSerializer < #....
belongs_to :author, always_include: true
end to notate that no matter who is rendering a comment, they also get the comment's author. This makes for some interesting notation nuances for other seralizers: BlogSerializer < #...
has_many :posts, include: [comments: :author]
end Here, the specification of author on comments is redundant and doesn't matter. BlogSerializer < #...
has_many :posts, include: [:comments]
end I think this is what would be the most ideal for this scenario, even though it's not explicit as to what's being included in the resulting json. |
@NullVoxPopuli, you actually raised another interesting point.
@joaomdmoura, do you like the schedule? |
@iMacTia @NullVoxPopuli glad I found this. 😀 I was just about to open an issue. This is exactly what we need out of AMS right now. I read through the issues. I think that BlogSerializer < #...
has_many :posts, include: :all_nested_associations
end IMO this: BlogSerializer < #...
has_many :posts, always_include: true
end Reads like "always include the Cons to this approach would mean that no one could have an association called @joaomdmoura we also need to think about how this would work for applying the JSON API spec which allows nested includes to be specified. See here. |
Hey everyone, I've being following it along and have just read through the last comments. @iMacTia the schedule look great, indeed, let's take some baby steps, have the Indeed @jfelchner, but the good news is that we already support neste associations following json-api conventions 😁. I think we all agree that this is aiming for the right direction, at least the first step on #1127. Let's get it merged then close this issue and open a new one to deal with |
One common pattern to avoid infinite loops in this case is to have a "light" serializer for non-primary resources (relevant for Json/FlattenJson, but not JsonApi). We could possibly make it so that primary resources are serialized according to An other solution is to define the whole tree of included resources at the adapter level, JsonApi-style. |
Including all nested associations was the default option in 0.8.x. |
Also, |
Finally,
First of all, the code is duplicated (we include |
@malroc, I understand your concern, and that you may be upset with this change. There has been quite a bit of discussion on this topic over the past months on various issues. To counter your argument for creating a separate 'light' serializer, for the association would result in something like this: Say, we have the objects If we wanted each association, but associations were deeply evaluated, that means we would need the following serializers for all scenarios - (which may be many people's case) CommentsSerializer
BlogSerializer
AuthorsSerializer
PostsSerializer
# everybody would at least have the above, regardless of implementation.
# but to have optionally nested associations in the way you describe, we would need:
CommentsWithAuthorSerializer
BlogWithPostsSerializer
PostsWithAuthorSerializer
AuthorsWithCommentsSerializer
AuthorsWithPostsSerializers
AuthorsWithPostsAndCommentSerializer
# and then for nesting associations
BlogWithPostsWithAuthorsAndWithCommentsWithAuthorsSerializer
PostsWithComentsWithAuthorsSerializer As you can see, this gets out of hand, pretty quickly.
include is the only option on the association that does any sort of manipulating on an association. render json: @objects, include: {...associations...} this is consistent with the JSON API spec and adapter
have a look at this: http://jsonapi.org/format/#fetching-includes
We will be sure to document as much as possible before the release of 0.10
we do include these, see this comment: #1127 (comment) |
@NullVoxPopuli yes, you are right on that, and actually that's the only issue with the old (0.8.x) approach (please compare to the number of issues of the new approach I listed above). |
Yes, but doing that for associations isn't the way to go. I'm working on some tests right now to demonstrate using flat serializers, and specifying nesting in the controller.
valid point -- the reason it's in the seralizers now, is because it was easier - a stepping stone to get to fuller functionality. This feature isn't final, and I plan to implement sideloading before RC4 |
@malroc here is the relevant commit that should address your issues: NullVoxPopuli@5806cb7 by default, nested associations are not rendered. So, specifying them in the controller would also allow for associations to be specified upon request from your frontend. |
So we step back to specifying nesting in the controllers? Again, why do we need serializers at all in that case? The first line of the AMS documentation states that:
And the new approach for handling nested associations breaks that. |
I believe if we take convention over configuration in the way you want it, we just wouldn't have any serializers, and it would all be generated... -.- |
(you can continue to use 0.8, btw - you don't have to upgrade) :-) |
The support for 0.8.x is already dropped, so I can't continue using it in the long run. |
It worked perfectly for 0.8.x, and I see no reason why it can't work now. |
I have a design in my head that could be pretty cool for nested JSON stuff: building on 0.8.x, and the idea I mentioned above, we could make it so that when a nested related resource gets serialized, let's say Post > Comment > Author, we would first look for class PostSerializer < ActiveModel::Serializer
attributes :id, :title
has_many :comments
class CommentSerializer < ActiveModel::Serializer
attributes :id, :content
belongs_to :author
class AuthorSerializer < ActiveModel::Serializer
attributes :id, :name
end
end
end Thoughts everybody? |
To be honest, I see use in both solutions:
|
@beauby +1 for your solution. |
@NullVoxPopuli Not really, because if your |
@beauby cool |
for clarification: class PostSerializer < ActiveModel::Serializer
attributes :id, :title
has_many :comments
# overrides the default / top level CommentSerializer
class CommentSerializer < ActiveModel::Serializer
attributes :id, :content
belongs_to :author
# by default - no nested relationships are rendered
# unless a serializer is specified here?
# this could also be class AuthorSerializer < ::AuthorSerializer,
# if we wanted it to be the same attributes, yeah?
class AuthorSerializer < ActiveModel::Serializer
attributes :id, :name
end
end
end |
Some news:
|
Let's continue this discussion in #1190. |
Nice, moving then, I'm closing this one then in favor of #1190 😄 |
I've been working on sideloading (#1107) for the json adapter, and needed recursive relationship building of the json object.
The problem with doing a strictly recursive approach (independent of sideloading or not), is that if you have enough references on your objects to other objects, a single request (of any kind) is basically going to return the whole database.
Example:
Post
belongs_to
anAuthor
whichhas_many
Post
s, and eachPost
has_many
Comment
s, and eachComment
belongs_to
anAuthor
.... it and would just keep going. The code in #1107 at least doesn't allow for things to be serialized multiple times (like if a post has the same author, that author only gets serialized once).So, a potential solution that I'm working on prototyping right now is something like this:
So, instead of recursively serializing the comments, we'd serialize them like we do today, but because we have the author key in the include hash, we'd also serialize each of the coments' authors.
resulting json may look like:
or
resulting json may look like:
this gets in to a problem of potentially having redundant data in the resulting json, so caching would be super important here (which I think is implemented by default? or does a cache key need to be specified in the controller? if not, should there be a default? - or would this kind of cacheing be separate, and more like fancy memoization?)
Note, I found that there was a previous attempt at solving this problem here: #952, but I think it's not clear in #952 how deep the nested relationships go / it could imply that the same recursive problem exists.
The text was updated successfully, but these errors were encountered: