#はじめに
Railsのプロジェクトで一部json形式でデータをレスポンスを返す必要があった時、インスタンス変数をメソッドで無理やりjsonにして関連付けレコードはmergeで無理やり連結、といった力技でコードを書いていました。
しかし、Active Model Serializerというgemを使えば簡単にjsonデータを整形できるとのことだったので試してみました。
#環境
ruby '2.5.1'
rails '5.2.4.1'
active_model_serializers '0.10.10'
#使用するモデル、アソシエーション
class Project < ApplicationRecord
has_many :jobs, dependent: :destroy
has_many :project_categories, dependent: :destroy
has_many :categories, through: :project_categories
end
class Job < ApplicationRecord
belongs_to :project
end
class ProjectCategory < ApplicationRecord
belongs_to :project
belongs_to :category
end
#Active Model Serializerの設定
以下のコマンドでModel
に紐づくSerializer
のファイルを作成できる
rails g serializer "モデル名"
まずはJobのserializer
ファイルを設定してみる
class JobSerializer < ActiveModel::Serializer
# 出力したいカラムを指定
attributes :id, :name
end
#Jsonを出力
コントローラに以下のコードを記述
def index
@job = Job.first
render json: @job, serializer: JobSerializer
end
出力結果
{
"id": 1,
"name": "ジョブ1"
}
期待した値が帰ってきました!
#複数のデータを出力する
1つのデータだけではなく複数のデータを出力したい場合は以下のようにコード記述
def index
@jobs = Job.all
render json: @jobs, each_serializer: JobSerializer
end
出力結果
[
{
"id": 1,
"name": "ジョブ1"
},
{
"id": 2,
"name": "ジョブ2"
},
{
"id": 3,
"name": "ジョブ3"
}
]
#ネストしたデータを出力する
Jobの親であるProjectも一緒に表示してみる
Projectのserializer
ファイルを下記のように設定
class ProjectSerializer < ActiveModel::Serializer
# 出力したいカラムを指定
attributes :id, :name
end
Jobのserializer
ファイルを下記のコードに修正
class JobSerializer < ActiveModel::Serializer
# 出力したいカラムを指定
attributes :id, :name
# アソシエーションの指定
belongs_to :project, serializer: ProjectSerializer
end
ネストされたデータを出力するとN+1問題が発生するので、controller側で下記のようにコードを修正
def index
# N+1問題の対応
@jobs = Job.preload(:project)
render json: @jobs, each_serializer: JobSerializer
end
出力結果
[
{
"id": 1,
"name": "ジョブ1",
"project": {
"id": 1,
"name": "テスト1"
}
},
{
"id": 2,
"name": "ジョブ2",
"project": {
"id": 2,
"name": "テスト2"
}
},
{
"id": 3,
"name": "ジョブ3",
"project": {
"id": 3,
"name": "テスト2"
}
}
]
#多重ネストしたデータを出力する
次はProjectと多対多の関係であるCategoryを表示してみる
Cagegoryのserializer
ファイルを下記のように設定
class CategorySerializer < ActiveModel::Serializer
# 出力したいカラムを指定
attributes :id, :name
end
Projectのserializer
ファイルを下記のように修正
class ProjectSerializer < ActiveModel::Serializer
# 出力したいカラムを指定
attributes :id, :name
# アソシエーションの指定
has_many :categories, serializer: CategorySerializer
end
出力結果
[
{
"id": 1,
"name": "ジョブ1",
"project": {
"id": 1,
"name": "テスト1"
}
},
{
"id": 2,
"name": "ジョブ2",
"project": {
"id": 2,
"name": "テスト2"
}
},
{
"id": 3,
"name": "ジョブ3",
"project": {
"id": 3,
"name": "テスト2"
}
}
]
Categoryがネストされていない。。。
どうやら多重ネストを出力したい場合は、render時にinclude
オプションを設定してあげる必要があるようです。
Jobのcontroller
を下記のように修正
def index
# N+1問題の対応
Job.preload(project: [:categories])
render json: @jobs, each_serializer: JobSerializer, include: { project: [ :categories] }
end
出力結果
[
{
"id": 1,
"name": "ジョブ1",
"project": {
"id": 1,
"name": "テスト1",
"categories": [
{
"id": 1,
"name": "北海道"
},
{
"id": 2,
"name": "東北"
},
{
"id": 5,
"name": "甲信越"
}
]
}
},
{
"id": 2,
"name": "ジョブ2",
"project": {
"id": 2,
"name": "テスト2",
"categories": [
{
"id": 7,
"name": "東海"
},
{
"id": 8,
"name": "関西"
},
{
"id": 9,
"name": "中国3"
},
{
"id": 10,
"name": "四国"
},
{
"id": 11,
"name": "九州・沖縄"
}
]
}
},
{
"id": 3,
"name": "ジョブ3",
"project": {
"id": 3,
"name": "テスト3",
"categories": [
{
"id": 4,
"name": "首都圏"
},
{
"id": 6,
"name": "北陸"
},
{
"id": 8,
"name": "関西"
},
{
"id": 9,
"name": "中国"
}
]
}
},
]
無事多重ネストしたデータを出力できました!
#参考URL
https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/configuration_options.md
https://tech.medpeer.co.jp/entry/2018/04/25/080000
https://techracho.bpsinc.jp/hachi8833/2017_09_28/45536
https://qiita.com/unasaka/items/08659477c72656df02e8
https://qiita.com/romukey/items/b78906e91a0355e1e77a
https://qiita.com/kakkunpakkun/items/1c23b936d13f08a42752
https://qiita.com/rh_taro/items/b09b3ebb68c2566c1ee5