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

Serialize only last record of a has_many association (as if it was a has_one association) #440

Open
YassineDM opened this issue Aug 19, 2019 · 5 comments

Comments

@YassineDM
Copy link

I have a user with many heights (stature measurements) recorded in a Height model but would like to serialize only last height (as if it was a has_many association).

I tried to create a (fake) custom has_one association but it does not give me what I was looking for...

app/serializers/user_serializer.rb

class UserSerializer < BaseSerializer
  attributes :email

  # has_many :heights
  has_one :last_height, record_type: :height do |user|
    user.heights.last
  end
end

app/controllers/users_controller.rb

options[:include] = [:heights]
render json: UserSerializer.new(user, options).

As is, I get the error: "heights is not specified as a relationship on UserSerializer."

If I uncomment # has_many :heights, I get though:

{
    "data": {
        "id": "1",
        "type": "user",
        "attributes": {
            "email": "[email protected]"
        },
        "relationships": {
            "heights": {
                "data": [
                    {
                        "id": "1",
                        "type": "height"
                    },
                    {
                        "id": "2",
                        "type": "height"
                    }
                ]
            },
            "lastHeight": {
                "data": {
                    "id": "2",
                    "type": "height"
                }
            }
        }
    },
    "included": [
        {
            "id": "1",
            "type": "height",
            "attributes": {
                "value": "186.0"
            }
        },
        {
            "id": "2",
            "type": "height",
            "attributes": {
                "value": "187.0"
            }
        }
    ]
}

But I don't want to include in the compound document all recorded heights...

Expected result

{
    "data": {
        "id": "1",
        "type": "user",
        "attributes": {
            "email": "[email protected]"
        },
        "relationships": {
            "lastHeight": {
                "data": {
                    "id": "2",
                    "type": "height"
                }
            }
        }
    },
    "included": [
        {
            "id": "2",
            "type": "height",
            "attributes": {
                "value": "187.0"
            }
        }
    ]
}
@hicklin-james
Copy link

hicklin-james commented Sep 27, 2019

@YassineDM - I have a very similar use case - did you come up with a workaround or otherwise solve this?

@hicklin-james
Copy link

hicklin-james commented Sep 27, 2019

Nevermind. I got this to work the way that I wanted. My structure is this:

class Parent
  has_many :children
end

class Child
  belongs_to :parent
end

class ParentController < ApplicationController
  def my_action
    parent = Parent.find(params[:id])
    child = parent.get_one_random_child

    options = {}
    options[:include] = [:child] # note -- this should be the same symbol as the has_one in your serializer
    options[:params] = {random_child: child}
    render json: MyParentSerializer.new(parent, options).serialized_json
  end
end

class MyParentSerializer
  include FastJsonapi::ObjectSerializer

  set_id :id
  set_type :parent

  attributes: ...

  has_one :child, serializer: MyChildSerializer do |object, params|
    params[:random_child]
  end
end

class MyChildSerializer
  include FastJsonapi::ObjectSerializer

  set_id :id
  set_type :child

  attributes: ...
end

The output matches your expected result above.

@YassineDM
Copy link
Author

@hicklin-james, shouldn't we be able to do this without options[:params] in the controller?

@hicklin-james
Copy link

hicklin-james commented Sep 27, 2019

Yes - you can put whatever you want in has_one block in the serializer. The key is that relation specified in the include: [] option matches the has_one symbol. I just tried this without the options[:params] and instead serialized object.children.first in the has_one block and it also worked like above.

@YassineDM
Copy link
Author

Thx @hicklin-james, I left it as a side note but will come back to it and let you know...

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

2 participants