active_model_serializers icon indicating copy to clipboard operation
active_model_serializers copied to clipboard

Documented alternative to 0.8 embed :ids, include: true in 0.10.x

Open goelinsights opened this issue 8 years ago • 16 comments

Expected behavior vs actual behavior

I know this is a breaking change. However, I'm unable to replicate what I could do previously and despite a number of pull requests cited in previous issues, the fix appears to be to use the JSON-API adapter, e.g., @NullVoxPopuli #1111

(note: I have a number of duplicate objects nested and I want to sideload the data in a redux store that then filters the object by id in the front-end (angular 2). Since this is a front-end site, I'm trying to limit API calls as much as possible and cache as much as I can in each request on the front-end, especially for mobile offline use. I find the JSON API format counter-productive to this use case (included relative to original is irrelevant and the format is overly verbose for what is already a large data pull), so I'm trying to do in AMS 0.10.x what I could do easily in 0.8.x with the commands embed :ids, include: true). I've tried embed: :ids at the serializer level and it seems to nest the data in my serialized JSON rather than insert the id and sideload the associated data.

Is this still possible in AMS 0.10.x or should I stick to 0.8 or try the initializer in 0.9 that was deprecated?

Example desired output with objects referenced by id and then sideloaded into the root by object name:

{
  "authors": 
  [
    {"id": 1, "name": "Ghost Writer", "book_ids":[1,2,5, 7, 8, 9, 10, 15]}, 
    {"id":2, "name": "Celebrity Nonwriter", "book_ids":[1,5,15], "publisher_id": 3}
  ],
  "books": 
   [
     {"id": 1, "title": "Art of the Deal", "author_ids": [1,2], "publisher_id": 3}
    ],
   "publishers": [
     {"id": 3, "name": "Ghostwriter Press"}
    ]
}

Steps to reproduce

(e.g., detailed walkthrough, runnable script, example application) tried to upgrade to 0.10.3 (via bundle update)

Environment

ActiveModelSerializers Version (commit ref if not on tag): 0.10.3

Output of ruby -e "puts RUBY_DESCRIPTION":

OS Type & Version:

Integrated application and version (e.g., Rails, Grape, etc): rails 4.2.6

Backtrace

(e.g., provide any applicable backtraces from your application)

Additonal helpful information

(e.g., Gemfile.lock, configurations, PR containing a failing test, git bisect results)

goelinsights avatar Dec 18 '16 01:12 goelinsights

That data structure requires a custom adapter, or perhaps an extension of the JSON adapter

bf4 avatar Dec 18 '16 17:12 bf4

@bf4 I'm wondering why that functionality was removed, which existed up until 0.9.5. Seems odd outside of the increasing tie between AMS and Ember data. If sideloading associated data is going to be problematic (and in the prior versions, this broke on caching), looks like Jbuilder is the right path forward for me, given deprecated support for the 0.8.x and 0.9.x branches and compatibility issues with Rails 5.

As I go deeper I'll need fragment caching, russian doll caching, sideloaded object caching with multi-fetch...all seem to be a bit outside the scope of the current capabilities of the project and I certainly don't want to swim upstream against the needs/ optimizations for the current community.

goelinsights avatar Dec 24 '16 17:12 goelinsights

Ams was a test bed for JSONAPI, which had embed pre1.0 but now is include

bf4 avatar Dec 24 '16 20:12 bf4

Does anyone have an adapter that allows "sideloading" so you can have multiple roots? I'm also forced to switch to jbuilder for the same reason.

rahilsondhi avatar Feb 01 '17 09:02 rahilsondhi

@rahilsondhi you're going to need to be more specific. JSON API allows sideloading. Do you mean a similar format to embed ids?

{
  "authors": 
  [
    {"id": 1, "name": "Ghost Writer", "book_ids":[1,2,5, 7, 8, 9, 10, 15]}, 
    {"id":2, "name": "Celebrity Nonwriter", "book_ids":[1,5,15], "publisher_id": 3}
  ],
  "books": 
   [
     {"id": 1, "title": "Art of the Deal", "author_ids": [1,2], "publisher_id": 3}
    ],
   "publishers": [
     {"id": 3, "name": "Ghostwriter Press"}
    ]
}

bf4 avatar Feb 01 '17 15:02 bf4

Sorry I guess I was confused about what "sideloading" means. But yes, I'm trying to achieve the JSON response you pasted - multiple keys at the root for different object types, like you have authors and books in your example.

I used to be able to achieve this in version 0.8.2, but it doesn't seem possible in 0.10.0.

class AlbumSerializerWithAssocs < AlbumSerializer
  root :album
  has_one :artist, serializer: ArtistSerializer, root: :artist, embed: :id, include: true
  has_many :tracks, serializer: TrackSerializer, root: :tracks, embed: :id, include: true
end
{
  "album": {attrs},
  "artist": {attrs},
  "tracks": [track, track, track]
}

rahilsondhi avatar Feb 01 '17 22:02 rahilsondhi

@rahilsondhi It's definitely possible. You can write any adapter you want. It doesn't come out of the box at this point. I'd support a PR to add an adapter or mixin that supports 'legacy ams'.

bf4 avatar Feb 01 '17 22:02 bf4

However, I think you could just add an attribute to the AuthorSerializer (any particular reason you changed the example to Albums?)

attributes :book_ids, :author_ids
has_many :authors
has_many :books

bf4 avatar Feb 01 '17 22:02 bf4

@rahilsondhi @goelinsights If you end up writing an adapter, ping me! It's the only thing holding me back from an upgrade.

pistachiomatt avatar Mar 02 '17 04:03 pistachiomatt

I went to jbuilder and am really happy.  Much better fit for what I'm working on.

Get Outlook for iOS

On Wed, Mar 1, 2017 at 8:45 PM -0800, "Matthew Farag" [email protected] wrote:

@rahilsondhi @goelinsights If you end up writing an adapter, ping me! It's the only thing holding me back from an upgrade.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

goelinsights avatar Mar 02 '17 04:03 goelinsights

You can get book ids in json by using example below:

attribute book_ids

def book_ids
  object.books.pluck_id
end

vaibhav8275 avatar Mar 17 '17 08:03 vaibhav8275

Anyone aware of a solution to this which will support embedded ids the way AMS 9 worked?

drewnichols avatar May 11 '17 20:05 drewnichols

@drewnichols could you help me with what's different in 0.9 vs. https://github.com/rails-api/active_model_serializers/issues/2006#issuecomment-276690478 ?

bf4 avatar May 11 '17 22:05 bf4

Sure, assuming we models Foo and Bar as follows:

class Foo < ActiveRecord::Base
  has_many :bar 
end

class Bar < ActiveRecord::Base
  belongs_to :foo
end

Using 9.x we could embed the Bar ids and include a key for the Bar objects as follows:

{
  "foo" : {
    "bar_ids" : [ 1, 2, 3 ]
  },
  "bars" : [
    { "id" : 1 },
    { "id" : 2 },
    { "id" : 3 }
  ]
}

It seams (and I'd love to discover i'm wrong) that the JSON adapter for 10 only supports the following style:

{
  "foo" : {
    "bars" : [
      { "id" : 1 },
      { "id" : 2 },
      { "id" : 3 }
    ]
  }
}

In this trivial example either will work ok however if you're hitting an index route of Foos and you want to include the Bars in the document you will have a number of repeated instances of Bar which can make the document rather large. For example:

{
  "foos" : [
    { 
      "id" : 1,
      "bars" : [
        { "id" : 1 },
        { "id" : 2 },
        { "id" : 3 }
      ]
    }, {
      "id" : 2,
      "bars" : [
        { "id" : 1 },
        { "id" : 2 },
        { "id" : 3 }
      ]
    }
  ]
}

This embedding ids style is super useful when working with, at least in my case, Ember projects as we can load a number of records together into Ember Data within 1 request. From what I can tell in AMS 10 I can get the array of embedded IDs by adding an attribute to the serializer with the suffix _ids like this:

class FooSerializer < ActiveModel::Serializer
  attributes :bar_ids
end

or I can get the array of child models like this:

class FooSerializer < ActiveModel::Serializer
  has_many :bars
end

but I can't get the embedded ids with the additional root key of "bars" containing an array of serialized Bars.

drewnichols avatar May 12 '17 13:05 drewnichols

@bf4 is there any workaround for this as of now?.

Anyone has custom adapter for this.

PratheepV avatar May 25 '17 10:05 PratheepV

@PratheepV It looks like one of us is going to have to setup up and make it happen.

drewnichols avatar May 25 '17 13:05 drewnichols