【Gem】ActiveModelSerializers
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
RailsでAPIモードを使用してJSONデータを受け取る時にActiveModelSerializers
というgemがとても重宝したので紹介します。JSONのレンダーを簡易的に行ってくれるので、Vue.jsなどを使用する場合は是非こちらのgemを使ってみてください。
インストール
現在の最新版を使用します。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_help
のyaml
ファイルで定義した値に変換することができます。元の値を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