はじめに
- Associations において利用する Serializer を切り替えました
- AMS で該当する機能は見つからなかったので素朴に実現しました
目次
- はじめに
- TL;DR
- 環境
- 構成
- Model
- API
- やりたいこと
- 実現方法
- Controller
- Serializer
- まとめ
- 参考
TL;DR
- Rendering の際に
options
を渡す
reservations_controller.rb
render json: reservation,
include: '**', restaurant_type: :detail
- Serializer の initialize で
options
を受け取る
reservation_serializer.rb
def initialize(object, options = {})
super
@restaurant_type = options[:restaurant_type]
-
options
の値によって 関連先で利用する Serializer を切り替える
reservation_serializer.rb
attributes :id, :restaurant
def restaurant
serializer = case @restaurant_type
when :base then RestaurantSerializer
when :detail then RestaurantDetailSerializer
else RestaurantSerializer
end
serializer.new(object.restaurant, @options)
環境
- ruby: 2.6.2
- rails: 5.2.3
- active_model_serializers: 0.10.9
構成
Model
app/models/restaurant.rb
# == Schema Information
#
# Table name: restaurants
#
# id :bigint(8) not null, primary key
# name :string not null
# phone :string not null
# address :string not null
class Restaurant < ApplicationRecord
has_many :reservations
app/models/reservation.rb
# == Schema Information
#
# Table name: reservations
#
# id :bigint(8) not null, primary key
# restaurant_id :bigint(8) not null
class Reservation < ApplicationRecord
belongs_to :restaurant
API
reservations_list.json
{
"reservations": [
{
"id": 42,
"restaurant": {
"id": 42,
"name": "Domino's Pizza"
}
}
]
}
reservation_detail.json
{
"reservation": {
"id": 42,
"restaurant": {
"id": 42,
"name": "Domino's Pizza",
"phone": "03-1234-5678",
"address": "東京都XXXXXX"
}
}
}
やりたいこと
- Reservations List API では restaurant の
id, name
のみを含める - Reservation Detail API では restaurant の
id, name, phone, address
を含める
実現方法
Controller
-
restaurant_type
を Serializers に渡す
reservations_controller.rb
module API
class ReservationsController < API::ApplicationController
def index
reservations = Reservation.all
render json: reservations,
include: '**', restaurant_type: :base
end
def show
reservation = Reservation.find(params[:id])
render json: reservation,
include: '**', restaurant_type: :detail
end
end
end
Serializers
- initialize で
options[:restaurant_type]
を受け取る -
options[:restaurant_type]
の値によって, Serializer を切り替える -
@options
を 渡すことで, options や scope を 引き継ぐ
reservation_serializer.rb
class ReservationSerializer < ActiveModel::Serializer
attributes :id, :restaurant
def initialize(object, options = {})
super
@options = options
@restaurant_type = options[:restaurant_type]
end
def restaurant
serializer = case @restaurant_type
when :base then RestaurantSerializer
when :detail then RestaurantDetailSerializer
else RestaurantSerializer
end
serializer.new(object.restaurant, @options)
end
end
restaurant_serializer.rb
class RestaurantSerializer < ActiveModel::Serializer
attributes :id, :name
end
restaurant_detail_serializer.rb
class RestaurantDetailSerializer < RestaurantSerializer
attributes :address, :phone
end
まとめ
- 今回は牧歌的な方法で切り替えました
- 良い方法があれば教えてください