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

Sparse Fieldset and Have Relationships #304

Closed
ritesh2741 opened this issue Aug 21, 2018 · 3 comments
Closed

Sparse Fieldset and Have Relationships #304

ritesh2741 opened this issue Aug 21, 2018 · 3 comments

Comments

@ritesh2741
Copy link

In given example with movie, actor and owner are 3 models with relationships
Let's say if I want only name from movie, and include only 1 attribute from actor and 1 attribute from owner, ( Let's assume actor has an attribute rating and owner has an attribute first_name as there aren't any given in example. )
I can use options[:include] = [:actor,:owner] and MovieSerializer.new(movie, options).serializable_hash to get the relationships and all the attributes that are present in all the models
Given the example for sparse fieldset, MovieSerializer.new(movie, { fields: { movie: [:name] } }) picks only the movie name which is good.

However I have some issues..

So, here are my problems and some workaround

  1. There isn't an example of how i can select only some attributes from my relationships. So what I did was options[:fields] = {:actor=>[:rating],:owner=>[:first_name]} and MovieSerializer.new(movie, options).serializable_hash gave me the required result. If this is correct please include this in the documentation.

  2. Given the example for sparse fieldset, MovieSerializer.new(movie, { fields: { movie: [:name] } }) will return just the object with relationships as empty hash. I cannot have scenario as mentioned above and this one together. So i figured a "not so good" way to tackle this

options[:include] = [:actor,:owner]
options[:fields] = {:actor=>[:rating],:owner=>[:first_name]}
res1 = MovieSerializer.new(movie, options).serializable_hash #returns the filtered response with proper fieldset
res2 = MovieSerializer.new(movie,{fields: {movie: [:name]}}).serializable_hash #returns {} for relationships
res2[:data][:relationships] = res1[:data][:relationships]

and the final result is in res2. However I am not sure if this is the right way. Please provide if there is a better way to do it.

Reference: " GET /articles?include=author&fields[articles]=title,body&fields[people]=name " which is from json api docs

@ritesh2741 ritesh2741 changed the title Sparse Fieldset and Have relationship Sparse Fieldset and Have Relationships Aug 21, 2018
@shishirmk
Copy link
Collaborator

@ritesh2741 You should be able to get the correct serialized output with just one call to the serializer. Have you tried something like this?

options[:include] = [:actor,:owner]
options[:fields] = {:actor=>[:rating],:owner=>[:first_name],:movie=>[:name]}
MovieSerializer.new(movie, options).serializable_hash

@ritesh2741
Copy link
Author

@shishirmk
The option you gave makes sense. The relationship hash is {} however the required results come inside included hash.

@BryceHarlan
Copy link

BryceHarlan commented Sep 25, 2018

Should the relationships hash return {} in the example discussed above? I've run into some issues using third party jsonapi normalizers because of this behavior. If not, the test to update would be it 'returns correct nested includes when serializable_hash is called' in object_serializer_spec.

EDIT
After doing some digging, I realized that you must specify associations in the fields option if you want fast_jsonapi to populate the relationships hash appropriately.

More explicitly, the following code

options[:include] = [:actor,:owner]
options[:fields] = {:actor=>[:rating],:owner=>[:first_name],:movie=>[:name]}
MovieSerializer.new(movie, options).serializable_hash

generates* the following output

{
  "data": {
    "id": "3",
    "type": "movie",
    "attributes": {
      "name": "test movie"
    },
    "relationships": { },
  }
  "included": [
    {
      "id": "1",
      "type": "actor",
      "attributes": {
        "rating": 5
      }
      "relationships": {}

    },
    ...
    {
      "id": "3",
      "type": "user"
      "attributes": {
        "first_name": "Jane"
      },
    }
  ]
}

But if we change the fields option to include :actors and :owners i.e.

options[:fields] = {:actor=>[:rating],:owner=>[:first_name],:movie=>[:name, :actors, :owner]}

The output includes associations in the relationships hash

{
  "data": {
    "id": "3",
    "type": "movie",
    "attributes": {
      "name": "test movie"
    },
    "relationships": {
      "actors" : {
        "data" : [
          {
            "id" : 1,
            "type" : "actor" 
          },
          ....
        ]
      },
      "owner" : {
        "id" : 3,
        "type" : "user" 
      }
    },
  }
  "included": [
    {
      "id": "1",
      "type": "actor",
      "attributes": {
        "rating": 5
      }
      "relationships": { }

    },
    ...
    {
      "id": "3",
      "type": "user"
      "attributes": {
        "first_name": "Jane"
      },
    }
  ]
}

I'm not sure if this should be considered a bug or not. Personally, I'd prefer if the relationships hash were implicitly populated with any included associations so that JSONAPI normalizers can always traverse the association graph easily. That said, using the sparse fields option kind of implies that you only want the serializer to return what you explicitly ask for. I'm not sure where the owners stand on this - if the current implementation is what you prefer, a small note in the documentation would help clarify

*Note: I didn't actually run the example code to generate this output - I figured this issue out within my application and typed out the analogous JSON here. My apologies if there are any discrepancies with the actual output as a result.

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

No branches or pull requests

3 participants