9

I am using the JSONAPI format along with Active Model Serializers to create an api with rails-api.

I have a serializer which shows a specific post that has many topics and currently, under relationships, lists those topics. It currently only lists the id and type. I want to show the title of the topic as well.

Some would say to use include: 'topics' in my controller, but I don't need the full topic record, just its title.

Question: How do I specify which attributes I want to show from topics?

What I have

"data": {
  "id": "26",
  "type": "posts",
  "attributes": {
    "title": "Test Title 11"
  },
  "relationships": {
    "topics": {
      "data": [
        {
          "id": "1",
          "type": "topics"
        }
      ]
    }
  }
}

What I want

"data": {
  "id": "26",
  "type": "posts",
  "attributes": {
    "title": "Test Title 11"
  },
  "relationships": {
    "topics": {
      "data": [
        {
          "id": "1",
          "type": "topics",
          "title": "Topic Title"
        }
      ]
    }
  }
}

My current serializer classes EDIT: this is what I'm after please.

class PostSerializer < ActiveModel::Serializer
  attributes :title

  belongs_to :domain
  belongs_to :user

  has_many :topics, serializer: TopicSerializer

  def topics
    # THIS IS WHAT I AM REALLY ASKING FOR
  end
end

class TopicSerializer < ActiveModel::Serializer
  attributes :title, :description

  belongs_to :parent
  has_many :children
end

One thing I tried - there is an answer below that makes this work, but its not really what I'm after.

class PostSerializer < ActiveModel::Serializer
  attributes :title, :topics

  belongs_to :domain
  belongs_to :user

  def topics
    # THIS WAS ANSWERED BELOW! THANK YOU
  end
end
alassiter
  • 505
  • 1
  • 5
  • 15

2 Answers2

7

Just make sure to return a hash or array of hashes like so:

def videos
    object.listing_videos.collect do |lv|
      {
        id: lv.video.id,
        name: lv.video.name,
        wistia_id: lv.video.wistia_id,
        duration: lv.video.duration,
        wistia_hashed_id: lv.video.wistia_hashed_id,
        description: lv.video.description,
        thumbnail: lv.video.thumbnail
      }
    end
  end
penner
  • 2,707
  • 1
  • 37
  • 48
  • I upvoted this answer b/c it did answer one of the problems I presented. I didn't mean to confuse this question, but after running into the same issue again, I realized I was unclear. I am not sure the etiquette on this, so please forgive me. The question was meant to be about specifying attributes on the relationship, see.. "What I want" and the answer here expands on one of the things I tried. It works, but what I really want is different. – alassiter Sep 30 '15 at 23:15
  • I don't see a good example of this in the docs so I would say that its not supported or doesn't work that way you'd like it to. It appears as though the function that overrides an association is intended to alter the scope and not the attributes. see here https://github.com/rails-api/active_model_serializers#user-content-overriding-association-methods . If you don't include or load the associated data in the controller wont it cause an N+1 in the serializer then? If you're using postgres you could return JSON from the db or massage the data in SQL and have a custom serializer. – penner Sep 30 '15 at 23:57
  • I got it to work, geez. This was the right answer after all. it works. If you have a has_many :topics, then object.topics.collect... works. If you have a belongs_to, you just return a single hash with the attributes you want. One thing I had to do was to remember that if I have a belongs_to :user, then I needed to use `object.user.name` to get the name. – alassiter Oct 01 '15 at 01:18
2

Instead of defining topics method, it's better to define separate topic serializer with explicitly specifying which attributes do you need to include. This is cleaner, and more maintainable approach then defining topics method.

class PostSerializer < ActiveModel::Serializer
  attributes :title

  belongs_to :domain
  belongs_to :user
  # remember to declare TopicSerializer class before you use it
  class TopicSerializer < ActiveModel::Serializer
    # explicitly tell here which attributes you need from 'topics'
    attributes :title
  end
  has_many :topics, serializer: TopicSerializer
end

Again, try to avoid defining methods for relations as much as possible, it's not clean, neither maintainable.

Parth Modi
  • 1,675
  • 2
  • 20
  • 26