rails-api / active_model_serializers

ActiveModel::Serializer implementation and Rails hooks
MIT License
5.33k stars 1.39k forks source link

proposed behavior for #filter using :attributes adapter #1895

Open nscricco opened 8 years ago

nscricco commented 8 years ago

Expected behavior vs actual behavior

Right now my application has a problem with deeply nested response bodies. Ideally, I would like to keep all the existing associations in the serializers and limit the attributes of nested associations in the controller.

(see below for full associations)

ActiveModelSerializers.config.adapter = :attributes
ActiveModelSerializers.config.default_includes = '**'

class PostsController < BaseController
  def index
    posts = Post.all.includes(:author, :comment)
    render json: posts, include: 'author', fields: { post: [:id, :title, :author], comment: [:id, :body] }
  end
end

response.body = {
  title: 
  author: {
    name:
    birthday:
  }
  comments: [
    {
      id:
      body:
    }
  ]
}

I know that in the past, the default serializer in Rails < 4.0 one could do the following:

render json: posts, include: {author: true, comment: [:id, :body]}, fields: [:id, :title]

Would it be useful for fields to be able to filter the attributes on nested associations?

Steps to reproduce

(e.g., detailed walkthrough, runnable script, example application)

I wrote a few tests that can be seen here

class PostSerializer < ActiveModel::Serializer
  attributes :title, :body
  belongs_to :author
  has_many :comments
end

class AuthorSerializer < ActiveModel::Serializer
  attributes :name, :birthday
end

class CommentSerializer < ActiveModel::Serializer
  attributes :body
  belongs_to :author
end

@author = Author.new(id: 1, name: 'Nick', birthday: '06.02.1988')
@comment1 = Comment.new(id: 7, body: 'cool', author: @author)
@comment2 = Comment.new(id: 12, body: 'awesome', author: @author)
@post = Post.new(id: 1337, title: 'Title 1', body: 'Body 1',
                                  author: @author, comments: [@comment1, @comment2])
@comment1.post = @post
@comment2.post = @post

fields = { posts: [:title, :author], comments: [:body] }
serializable = ActiveModelSerializers::SerializableResource.new(@post, adapter: :attributes, fields: fields, include: 'comments,author').serializable_hash
expected = [
  title: 'Title 1',
  author: {
    name: 'Nick',
    birthday: '06.02.1988'
  },
  comments: [
    {
      id: '7',
      body: 'cool'
    }, {
      id: '12',
      body: 'awesome'
    }
  ]
]

Environment

ActiveModelSerializers Version (commit ref if not on tag): 2423ca49995a

Output of ruby -e "puts RUBY_DESCRIPTION": ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]

OS Type & Version: OS X 10.11.4 (15E2066)

NullVoxPopuli commented 8 years ago

it looks like you need to include author as well in your include parameter.

bf4 commented 8 years ago

Would it be useful for fields to be able to filter the attributes on nested associations?

Is this basically backporting JSON API adapter behavior to the Attributes adapter?

nscricco commented 8 years ago

Yes, exactly. If you think it's worth adding I am happy to put up a PR for it.

NullVoxPopuli commented 8 years ago

This just further proves the need to unify everything.

bf4 commented 8 years ago

Very closely related to https://github.com/rails-api/active_model_serializers/issues/1843

@nscricco Since there's a lot involved, might be best to pick something small and make a lot of small prs. Join us on the amserializers.herokuapp.com slack to discuss .