値や配列などnon-ActiveRecord オブジェクトのシリアライズをする(Rails7)

Rails・Webシステム開発
Anja🤗#helpinghands #solidarity#stays healthy🙏によるPixabayからの画像
スポンサーリンク

RailsでAPIを作成するためにActiveModel::Serializerを使うことは多いかと思います。

普通にActiveRecordのオブジェクト(User.find(hoge)とかで取得したオブジェクト)を引き渡す場合は特に問題ないのですが、純粋な値や配列を引き渡そうとすると、NoMethodErrorを発生します。

しかし、意外と簡単な解決方法がありましたので紹介します。



事の起こり

app/serializers/user_serializer.rb:

class UserSerializer < ActiveModel::Serializer
  attribute(:id) { object&.dig('id') }
  attribute(:name) { object&.dig('name') }
end

上記のようなシリアライザ−をセットして、下記コントローラで外部APIから取得したデータの一部をJson形式で返却するAPIを実装したい。

app/controllers/users_controller.rb:

class UserController < ApplicationController
  def index
    users = (外部APIで情報を取得。Json形式で値が返却される。)
    render json: users, each_serializer: UserApiSerializer
  end
end

ただ、これだと下記エラーが出ます。

NoMethodError (undefined method `model_name’ for〜のエラー

NoMethodError (undefined method `model_name' for Hash:Class

          object.class.model_name.to_s.underscore
                      ^^^^^^^^^^^
Did you mean?  module_parent):

「ハッシュに対してmodel_nameは定義されてないよ」

つまり、ActiveModelが、ActiveRecordのオブジェクトが引き渡されたと勘違いしている。

解決策

下記記事で紹介している「ホ1:ルートキーの変更」と同様の対応で解決できます。

つまり、

app/serializers/user_serializer.rb:

class UserApiSerializer < ActiveModel::Serializer
  type 'user_api'
  attribute(:id) { object&.dig('id') }
  attribute(:name) { object&.dig('name') }
end

typeを設定してあげればOK。これでActiveModelが「ActiveRecordのオブジェクトとは限らないのでは・・・!」と認識してくれるっぽい。

ルートキーをResponse Bodyに含めたくない場合は、コントローラーでadapter: :attributesを設定しておけば問題ありません。(デフォルトで設定されていれば、このままでOK)

はるすと
はるすと

最後まで読んでくださってありがとうございました!

この記事を書いた人
こもれびエンジニア

自然と自由を愛するエンジニア。2021年1月に、大手製造業設計からプログラマ(Rails, AWS)へ転職。動物や自然との触れ合いや、汗を流すのが好き。

/HSP(繊細さん)/18デリケートな象/ストレングスファインダー(1分析思考/2親密性/3学習欲/4調和性/5収集心)、テニス、合気道、登山、あいだみつを、ジブリ、ワンピース、ドラゴンボール、AWS、Ruby on Rails、アイミング

twitterをフォローして、記事にならないちょっとした豆知識もチェック!
Rails・Webシステム開発
スポンサーリンク
SNSでシェア/コメントして、自分のアウトプット/発信力を高めるのにお使いください。 ↓ 各ページへジャンプ ↓
twitterをフォローして、記事にならないちょっとした豆知識もチェック!
スポンサーリンク
「そんなか」サイト

コメント

タイトルとURLをコピーしました