大ちゃんの駆け出し技術ブログ

RUNTEQ受講生のかわいいといわれるアウトプットブログ

【Gem】ActiveModelSerializers

はじめに

こんにちは!大ちゃんの駆け出し技術ブログです。

RailsAPIモードを使用してJSONデータを受け取る時にActiveModelSerializersというgemがとても重宝したので紹介します。JSONのレンダーを簡易的に行ってくれるので、Vue.jsなどを使用する場合は是非こちらのgemを使ってみてください。

github.com

インストール

現在の最新版を使用します。Gemファイルに記載し、bundle install

# Gemfile
gem 'active_model_serializers', '~> 0.10.0'
$ bundle install
Installing jsonapi-renderer 0.2.2
Installing case_transform 0.2
Fetching active_model_serializers 0.10.12
Installing active_model_serializers 0.10.12

JSONを返却するシリアライザーを作成します。作成方法は下記コマンドのとおりです

$ rails g serializer モデル名

今回自分がこちらのgemで使用したのはUserモデルとProfileモデルですので、下記のようにコマンドを実行しました。コマンド実行後、app/serializers/配下にシリアライザ用のファイルが作成されます。

$ rails g serializer User
      create  app/serializers/user_serializer.rb
$ rails g serializer Profile
      create  app/serializers/profile_serializer.rb

デフォルトでは中身がattributes :idと記載されているファイルが作成されます。

class User < ActiveModel::Serializer
  attributes :id
end

attributesにはJSONで取得したい値を記述します。今回自分のUserモデルでは:nameカラム、:imageカラムを取得したいので追加しました。

class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :image
end

また、アソシエーション関係にあるJSONの値も取得したい場合、アソシエーションを表すhas_many, has_oneなどを記載すればアソシエーション関連の値も取得できます。

class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :image
    has_one :profile
end

Profileモデルの属性も同様に指定します。

class ProfileSerializer < ActiveModel::Serializer
  attributes :id, :birthday, :day_of_joinning, :height, :gender, :blood_type, :prefecture_id
  belongs_to :user
end

以上で準備完了です!

ほんとにお手軽です!

使用例

ではJSONを実際に取得してみます。

  • User情報一覧の取得の場合

http://localhost:3000/api/v1/users/

def index
  @users = User.all
  render json: ActiveModel::Serializer::CollectionSerializer.new(
    @users,
    serializer: UserSerializer
  ).to_json
  end

ActiveModel::Serializer::CollectionSerializerが気になるかと思いますが、リソースの集合をシリアライザで表現する時に使用するようです。だいたいリソースが複数となるindexアクションでしか使用しません。serializer: UserSerializerとすることで使用するシリアライザを指定します。

返却されるJSON

[{"id":8,"name":"大ちゃん","image":"~~~~省略~~~~-192.png","profile":{"id":5,"birthday":"2021-04-02T00:00:00.000Z","day_of_joinning":"2021-04-04T00:00:00.000Z","height":154,"gender":"male","blood_type":"B","prefecture_id":13}}]

現在UserのレコードとProfileのレコードが1つずつしかないので返却される値は少なく見えますが、ちゃんと値が返ってきています。

  • Profile一覧の取得の場合

http://localhost:3000/api/v1/profiles/

def index
   @profiles = Profile.all
  render json: ActiveModel::Serializer::CollectionSerializer.new(
    @profiles,
    serializer: ProfileSerializer
  ).to_json
end

返却されるJSON

[{"id":5,"birthday":"2021-04-02T00:00:00.000Z","day_of_joinning":"2021-04-04T00:00:00.000Z","height":154,"gender":"male","blood_type":"B","prefecture_id":13,"user":{"id":8,"name":"大ちゃん","image":"~~~~省略~~~~-192.png"}}]

リアライザーの値をメソッドで定義する

返却するJSONの値をシリアライザ側で加工して渡すこともできます。

例えば、先ほど取得したプロフィールカラムの性別を日本語に加工したいとしましょう。

"gender":"male""gender":"男性"

この場合enum_helpを使用していることが前提になります。

# config/locales/enums.ja.yml
ja:
  enums:
    profile:
      gender:
        male: "男性"
        female: "女性"

リアライザで変換したい場合、以下のようにメソッドを定義することで変換が可能です。

class ProfileSerializer < ActiveModel::Serializer
  attributes :id, :birthday, :day_of_joinning, :height, :gender, :blood_type, :prefecture_id
  belongs_to :user

  def gender
    object.gender_i18n
  end
end

objectはモデル自体です。gender_i18nとすることでenum_helpyamlファイルで定義した値に変換することができます。元の値をJSONファイルで取得する必要がない場合、attributes自体に変更は必要ありません。これで日本語に変換された値を取得できます。

プロフィールのprefecture_idも変換します。prefecture_idはActive Hashを使用して定義しています。

class Prefecture < ActiveHash::Base
  self.data = [
    { id: 1, name: '北海道' }, { id: 2, name: '青森県' }, { id: 3, name: '岩手県' },
    { id: 4, name: '宮城県' }, { id: 5, name: '秋田県' }, { id: 6, name: '山形県' },
    { id: 7, name: '福島県' }, { id: 8, name: '茨城県' }, { id: 9, name: '栃木県' },
    { id: 10, name: '群馬県' }, { id: 11, name: '埼玉県' }, { id: 12, name: '千葉県' },
    { id: 13, name: '東京都' }, { id: 14, name: '神奈川県' }, { id: 15, name: '新潟県' },
    { id: 16, name: '富山県' }, { id: 17, name: '石川県' }, { id: 18, name: '福井県' },
    { id: 19, name: '山梨県' }, { id: 20, name: '長野県' }, { id: 21, name: '岐阜県' },
    { id: 22, name: '静岡県' }, { id: 23, name: '愛知県' }, { id: 24, name: '三重県' },
    { id: 25, name: '滋賀県' }, { id: 26, name: '京都府' }, { id: 27, name: '大阪府' },
    { id: 28, name: '兵庫県' }, { id: 29, name: '奈良県' }, { id: 30, name: '和歌山県' },
    { id: 31, name: '鳥取県' }, { id: 32, name: '島根県' }, { id: 33, name: '岡山県' },
    { id: 34, name: '広島県' }, { id: 35, name: '山口県' }, { id: 36, name: '徳島県' },
    { id: 37, name: '香川県' }, { id: 38, name: '愛媛県' }, { id: 39, name: '高知県' },
    { id: 40, name: '福岡県' }, { id: 41, name: '佐賀県' }, { id: 42, name: '長崎県' },
    { id: 43, name: '熊本県' }, { id: 44, name: '大分県' }, { id: 45, name: '宮崎県' },
    { id: 46, name: '鹿児島県' }, { id: 47, name: '沖縄県' }
  ]
end

Active Hashへの値の変換方法は[object.prefecture.変換したい値]とすることで変換できます。今回の場合、object.prefecture.idでPrefectureモデルで指定した都道府県ID、object.prefecture.name都道府県名を取得できます。今回は都道府県名を取得します。

# app/serializers/profile_serializer.rb
class ProfileSerializer < ActiveModel::Serializer
  attributes :id, :birthday, :day_of_joinning, :height, :gender, :blood_type, :prefecture_id
  belongs_to :user

  def gender
    object.gender_i18n
  end

  def prefecture_id
    object.prefecture.name
  end
end

最後に日付のフォーマットも整形したいですね。不格好です。

"birthday":"2021-04-02T00:00:00.000Z"
"day_of_joinning":"2021-04-04T00:00:00.000Z"

strftimeメソッドを使えば簡単にフォーマットを整形できます。

# app/serializers/profile_serializer.rb
class ProfileSerializer < ActiveModel::Serializer
  attributes :id, :birthday, :day_of_joinning, :height, :gender, :blood_type, :prefecture_id
  belongs_to :user

  def gender
    object.gender_i18n
  end

  def prefecture_id
    object.prefecture.name
  end

  def birthday
    object.birthday.strftime("%F")
  end

  def day_of_joinning
    object.day_of_joinning.strftime("%F")
  end
end

参考文献

github.com

qiita.com