なにこれ
RailsのActiveModel::Serializersを使っている時に、
2つのjsonオブジェクトをrenderしたい時、片方だけにネストさせたり、あえてネストさせない方法を
自分への備忘録として書く記事です。
この記事で得られること
Railsのメソッド内で下記みたいなコードを書く時に、ネストさせたいオブジェクトを
好きなようにいじくる方法を知れます。
render json: { hoge: hoge, fuga: fuga }, include: :piyo
答え
Railsのas_jsonメソッド
を使えば解決する!
環境
Ruby 2.6
Rails 6.0
active_model_serializers 0.10.10
前提
下記4つのモデルがあります。
- user.rb
- facility.rb
- product.rb
userとfacilityは1:N、
productモデルとuserモデルはアソシエーション組んでません。
今回はカラムは必要ないので省略。
class User < ApplicationRecord
belongs_to :facility
end
class Facility < ApplicationRecord
has_many :users
end
class Product < ApplicationRecord
# アソシエーションなし
end
状況
users#showアクションを呼び出した時に、
userとproductsオブジェクトの2つをrenderする必要があり、
userのネストしたオブジェクトも取り出す必要がある。
実際に遭遇したエラー
def show
user = User.find(params[:id])
products = Product.all
render json: { user: user, products: products }, include: ['facility']
end
上記のコードで書いてレスポンスを受け取る時、下記のエラーが発生します!
NoMethodError (undefined method `facility' for #<Product:0x00007fed8d885518>):
renderするjsonオブジェクトにincludeすることを明示しました。
でも、上記の書き方だと
userオブジェクトとproductsオブジェクトの両方にfacilityがネストされてしまう。
productsモデルとfacilityモデルはアソシエーションを組んでいないので、
当然そんなメソッド(モデル?)はないので、
Railsは「facility?そんなメソッド知らないよ!」と言ってしまう。
じゃあどうするか
結論
変数を代入するタイミングでas_jsonメソッドを使ってjson整形して、
includeでfacilityモデルをネストすることを明示する。
def show
user = User.find(params[:id]).as_json(include: 'facility')
products = Product.all
render json: {user: user, products: products}, status: :ok
end
こうすることで、変数userだけにfacilityモデルをネストすることができます。
応用編
もし、「facilityモデルが不要なので、facilityモデルをネストさせないでデータを取得したい!」って時は、こうする。
def show
user = User.find(params[:id]).as_json
products = Product.all
render json: {user: user, products: products}, status: :ok
end
記述方法は分かったけど、正直なぜネストされないでjsonが整形されるかは分からないです。
勝手にネストされるときとネストされない時がある?
ここは要検証
おまけ:as_jsonのオプションはActiveModel as_json のオプション一覧という記事で詳しく解説してくれています。ありがたや〜
Railsのas_jsonメソッドの公式ドキュメントはこちら:https://railsdoc.com/page/as_json