Crazy Coders
Crazy Coders

Reputation: 133

Rails - How get first record of associated table?

i am trying to create an api for my mobile app.

I have posts and images tables. For my api, i can send all posts with:

@posts = Post.all render json: @posts

Output: [{"id":20,"title":"Title 1", "body":" first post ", "user_id":1 }]

But it does not contain images at all. In order to show a showcase image in homepage of my app, i just need the first image of associated images.

The output which i need is (the name of showcase_image attribute does not matter) :

Output: [{"id":20, "title":"Title 1", "body":" first post ", "showcase_image": 'first_image.jpg' , "user_id":1 }]

I need to include first image from associated images table to my json response..

Thanks in advance !

Upvotes: 1

Views: 800

Answers (2)

3limin4t0r
3limin4t0r

Reputation: 21120

You can include associations with the :include option when calling as_json.

render json: @posts.as_json(include: :images)

You could limit this to one image by adding a new association to Post.

class Post < ApplicationRecord
  has_many :images
  has_one :showcase_image, class_name: 'Image'
end

This would allow you to use the :showcase_image instead.

render json: @posts.as_json(include: :showcase_image)

You could also use Jbuilder to solve the issue at hand without adding an additional association.

# app/views/posts/index.json.jbuilder

# Get images that belong to posts, group them by post_id and
# return the minimum image id for each post_id.
images = Images.where(post_id: @posts.select(:id)).group(:post_id).minimum(:id)
# Request the full image data for all image ids returned above.
images = images.keys.zip(Image.find(images.values)).to_h

json.array! @posts do |post|
  json.extract! post, :id, :title, :body, :...

  json.showcase_image do
    image = images[post.id]
    if image
      json.extract! image, :id, :name, :location, :...
    else
      json.null!
    end
  end
end

Without calling a specific render, Rails will default to the app/views/posts/index file, and select the file matching the request. (If you request HTML it will look for an HTML file, if you request JSON it looks for JSON, etc.)

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  def index
    @posts = Post.all
  end
end

Now when you request /posts.json or /posts with the header Accept: application/json your application should return the JSON response build by Jbuilder.

Upvotes: 0

Felipe Sadoyama
Felipe Sadoyama

Reputation: 182

I would suggest using a serializer. Active Model Serializer is pretty standard and easy to use, but is not receiving any updates and has a bad performance. You can choose any other serializer (I recommend Blueprinter) or use the AMS. Through the AMS you coudl define the relation you want to serialize and it would build the json you're expecting

class PostSerializer < ActiveModel::Serializer
  attributes :id, :title, :body, :showcase_image, :user_id

  def showcase_image
    object.images.first.name # don't know what is the attribute you're looking for
  end
end

And on your controller:

@posts = Post.includes(:images).all # Use includes to avoid N+1 problems
render json: @posts, serialize_collection: PostSerializer

Upvotes: 3

Related Questions