はじめに
Rails のモデルには to_json という便利なメソッドがあります。このメソッドはデフォルトでは、すべてのカラムとその値を含む json 文字列を返します。ただ、値を少し加工したい場面がほとんどです。たとえば下記のような要求が出てくると思います。
- 特定のカラムを出力しないようにする
- 特定のカラムだけを出力する
- メソッドの結果をカラムのようにして出力する
- 関連モデルを出力する
- 関連モデルの特定カラムを出力する or 特定カラムを出力しない
- 関連モデルのさらに関連モデルを出力する
上記のことを実現するのに to_json の呼び出し先の as_json をオーバーライドする方法や、独自メソッドを定義する方法、 jbuilder を使う方法などが紹介されているようです。しかしながら、上記の要求であれば to_json のオプションだけで実現できます。
基本
以降は Book というモデルを例にして紹介していきます。 to_json はモデルに定義されているほか、リレーションにも定義されています。
Book.first.to_json
Book.all.to_json
特定のカラムを出力しないようにする
オプションの except を使います。たとえば id と body を出力しない場合は下記のようにします。例では配列を引数にしていますが、要素数が1個の場合は配列の中身を直接指定できます。以降のメソッドも同様です。
Book.first.to_json(except: [:id, :body])
特定のカラムだけを出力する
オプションの only を使います。たとえば title だけを出力したい場合は下記のようにします。
Book.first.to_json(only: [:title])
メソッドの結果をカラムのようにして出力する
オプションの methods を使います。たとえば Book に要約を出力するインスタンスメソッド summary が定義されている場合は下記のようにします。
Book.first.to_json(methods: [:summary])
Book.first.to_json(methods: [:summary], only: [:summary]) # summary 以外出力しない
関連モデルを出力する
オプションの include を使います。たとえば Book に belongs_to の関連モデル Author, Publisher が定義されている場合は下記のようにします。has many 関係でも同様にできます。 has many through の関係でも同様です。
Book.first.to_json(include: [:author, :publisher])
関連モデルの特定カラムを出力する or 特定カラムを出力しない
オプションの include を使います。そして include 先のモデルにもオプションを与えます。たとえば、上記の例において Publisher モデルの名前 name 以外のカラムが不要な場合は下記のようにします。
Book.first.to_json(include: [:author, {publisher: {only: :name}}])
例は省略しますが except も同じように使うことができます。
関連モデルのさらに関連モデルを出力する
オプションの include を使います。そして include 先のモデルにもオプションを与えます。たとえば、 Book -> Author -> Group の順に has one 関連モデルを持っていて、その階層関係すべてを出力する場合は下記のようにします。
Book.first.to_json(include: {author: {include: :group}})
まとめ
to_json のオプション引数を使って、いろいろな要求が実現できることを紹介しました。オプション only, except, method, include, はすべて組み合わせ可能です。たとえば下のような複雑なオプションを与えることができます。
Book.all.to_json(
only: [], # Book のカラムはすべて不要
include: {
publisher: {} # Publisher のオプションは無し(デフォルトで出力)
author: {
include: {
group: { # Group は group_name メソッドの結果だけ出力
methods: :group_name
only: :group_name
}
}
}
}
)
可読性や実行速度、再利用性を重視する場合は Jbuilder や Fast JSON API や jsonapi-rb などを使うべきかもしれません。