Ruby on Rails での、json 出力について調べたのでまとめます。
目次
実行環境
モデル
scaffold でモデル他作成
User モデルと User モデルが、複数の Itme を持つものとする。
生成のコマンドは以下の通り、json 出力をテストしたいだけなので、scaffold で作成する。
1 2 3
| rails generate scaffold user name:string rails generate scaffold item name:string price:integer user:references rails db:migrate
|
各モデルのアソシエーションは以下の通り。
app/models/user.rb1 2 3
| class User < ApplicationRecord has_many :items end
|
app/models/item.rb1 2 3
| class Item < ApplicationRecord belongs_to :user end
|
データの用意
適当なデータを作ります。
今回は seed で作成してみる。
db\seeds.rb を以下のように編集した。
seeds.rb1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Item.delete_all() User.delete_all()
user1 = User.create(name:"User1") Item.create(name:"item1",price:100,user:user1) Item.create(name:"item2",price:200,user:user1)
user2 = User.create(name:"User3") Item.create(name:"item3",price:300,user:user2) Item.create(name:"item4",price:400,user:user2)
user3 = User.create(name:"User3") Item.create(name:"item5",price:500,user:user3) Item.create(name:"item6",price:600,user:user3)
|
こちらを作成して、rails db:seed
で反映し、rails s
でアプリケーションを開始する。
以後は、/users.json にアクセスしたときの動作をもとに確認するものとする。
確認
用意されている json でのレスポンス
scaffold で用意したことである程度のものがそろっている。
User コントローラを開くと以下のようになっています。
app/controllers/users_controller.rb1 2 3 4 5 6
| class UsersController < ApplicationController def index @users = User.all end
|
ブラウザで/users.json にアクセスすると、以下のレスポンスになります。
1 2 3
| [ {"id":1,"name":"User1","created_at":"2019-11-05T14:03:54.753Z","updated_at":"2019-11-05T14:03:54.753Z","url":"http://localhost:3000/users/1.json"},{"id":2,"name":"User2","created_at":"2019-11-05T14:03:54.805Z","updated_at":"2019-11-05T14:03:54.805Z","url":"http://localhost:3000/users/2.json"},{"id":3,"name":"User3","created_at":"2019-11-05T14:03:54.831Z","updated_at":"2019-11-05T14:03:54.831Z","url":"http://localhost:3000/users/3.json"} ]
|
これは、app/views/users/index.json.jbuilder によって処理された結果になっている。
app/views/users/index.json.jbuilder 配下の通り。
app/views/users/index.json.jbuilder1
| json.array! @users, partial: "users/user", as: :user
|
コントローラで定義した@users が index.json.jbuilder で展開されているのがわかる。
jbuilder でも部分テンプレート partial が使用されている。
この部分テンプレートは app/views/users/_user.json.jbuilder が該当する。
app/views/users/_user.json.jbuilder1 2
| json.extract! user, :id, :name, :created_at, :updated_at json.url user_url(user, format: :json)
|
もっとシンプルに json 出力
app/views/users/index.json.jbuilder を適当にリネームする。
以下のように編集する。
app/controllers/users_controller.rb1 2 3 4 5 6 7
| class UsersController < ApplicationController
def index @users = User.all render json: @users end
|
ブラウザで/users.json にアクセスすると、以下のレスポンスになります。
1 2 3 4 5
| [ {"id":1,"name":"User1","created_at":"2019-11-05T14:03:54.753Z","updated_at":"2019-11-05T14:03:54.753Z"}, {"id":2,"name":"User2","created_at":"2019-11-05T14:03:54.805Z","updated_at":"2019-11-05T14:03:54.805Z"}, {"id":3,"name":"User3","created_at":"2019-11-05T14:03:54.831Z","updated_at":"2019-11-05T14:03:54.831Z"} ]
|
特定のカラムだけ json 出力
特定のカラムだけを吐き出したいことがあります。
selest メソッドを使用して、user.id と user.name だけを出力する場合以下のようになります。
app/controllers/users_controller.rb1 2 3 4 5 6
| class UsersController < ApplicationController def index @users = User.all render json: @users.select(:id,:name) end
|
ブラウザで/users.json にアクセスすると、以下のレスポンスになります。
1 2 3 4 5
| [ {"id":1,"name":"User1"}, {"id":2,"name":"User2"}, {"id":3,"name":"User3"} ]
|
関連モデルを json 出力 join 編
User に関連付けされている Item もまとめて json で出力してみます。
users テーブルと、items テーブルを連結してから出力する場合は以下のようにできます。
app/controllers/users_controller.rb1 2 3 4 5 6 7
| class UsersController < ApplicationController
def index @users = User.joins(:items).select('users.*,items.*') render json: @users end
|
ブラウザで/users.json にアクセスすると、以下のレスポンスになります。
1 2 3 4 5 6 7
| [ {"id":1,"name":"item1","created_at":"2019-11-05T14:03:54.786Z","updated_at":"2019-11-05T14:03:54.786Z","price":100,"user_id":1}, {"id":2,"name":"item2","created_at":"2019-11-05T14:03:54.795Z","updated_at":"2019-11-05T14:03:54.795Z","price":200,"user_id":1}, {"id":3,"name":"item3","created_at":"2019-11-05T14:03:54.813Z","updated_at":"2019-11-05T14:03:54.813Z","price":300,"user_id":2}, {"id":4,"name":"item4","created_at":"2019-11-05T14:03:54.822Z","updated_at":"2019-11-05T14:03:54.822Z","price":400,"user_id":2}, {"id":5,"name":"item5","created_at":"2019-11-05T14:03:54.839Z","updated_at":"2019-11-05T14:03:54.839Z","price":500,"user_id":3}, {"id":6,"name":"item6","created_at":"2019-11-05T14:03:54.848Z","updated_at":"2019-11-05T14:03:54.848Z","price":600,"user_id":3}]
|
出力ができましたが、どうやら User の id と name のカラムが item の id と name で上書きされるようです。
誤算でした。
この対応としてカラムに別名をつければいいようです。
.select('users.*,items.id as items_id,items.name as items_name')
のようにすることで回避できました。
app/controllers/users_controller.rb1 2 3 4 5 6 7
| class UsersController < ApplicationController
def index @users = User.left_outer_joins(:items).select('users.*,items.id as items_id,items.name as items_name') render json: @users end
|
ブラウザで/users.json にアクセスすると、以下のレスポンスになります。
1 2 3 4 5 6 7 8
| [ {"id":1,"name":"User1","created_at":"2019-11-05T14:03:54.753Z","updated_at":"2019-11-05T14:03:54.753Z","items_id":1,"items_name":"item1"}, {"id":1,"name":"User1","created_at":"2019-11-05T14:03:54.753Z","updated_at":"2019-11-05T14:03:54.753Z","items_id":2,"items_name":"item2"}, {"id":2,"name":"User2","created_at":"2019-11-05T14:03:54.805Z","updated_at":"2019-11-05T14:03:54.805Z","items_id":3,"items_name":"item3"}, {"id":2,"name":"User2","created_at":"2019-11-05T14:03:54.805Z","updated_at":"2019-11-05T14:03:54.805Z","items_id":4,"items_name":"item4"}, {"id":3,"name":"User3","created_at":"2019-11-05T14:03:54.831Z","updated_at":"2019-11-05T14:03:54.831Z","items_id":5,"items_name":"item5"}, {"id":3,"name":"User3","created_at":"2019-11-05T14:03:54.831Z","updated_at":"2019-11-05T14:03:54.831Z","items_id":6,"items_name":"item6"} ]
|
今度は,User の id と name,Item の id と name を別に出力できました。
テーブルを連結しているので、シンプルに特別構造化されることもない出力になりました。
カラム数が多くなってくると個別に別名をつけるのはやや面倒そうです。
関連モデルを json 出力 jbuilder 編
今度は User に関連付けされている Item もまとめて jbuilder で json を作って出力してみます。
app/controllers/users_controller.rb1 2 3 4 5 6 7
| class UsersController < ApplicationController
def index @users = User.all end
|
app/views/users/index.json.jbuilder1
| json.array! @users ,partial: "users/user" ,as: :user
|
app/views/users/_user.json.jbuilder1 2 3 4
| json.extract! user, :id, :name, :created_at, :updated_at json.items do json.array! user.items end
|
ブラウザで/users.json にアクセスすると、以下のレスポンスになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| [ { "id":1,"name":"User1","created_at":"2019-11-05T14:03:54.753Z","updated_at":"2019-11-05T14:03:54.753Z", "items":[ {"id":1,"name":"item1","price":100,"user_id":1,"created_at":"2019-11-05T14:03:54.786Z","updated_at":"2019-11-05T14:03:54.786Z"}, {"id":2,"name":"item2","price":200,"user_id":1,"created_at":"2019-11-05T14:03:54.795Z","updated_at":"2019-11-05T14:03:54.795Z"} ] }, { "id":2,"name":"User2","created_at":"2019-11-05T14:03:54.805Z","updated_at":"2019-11-05T14:03:54.805Z", "items":[ {"id":3,"name":"item3","price":300,"user_id":2,"created_at":"2019-11-05T14:03:54.813Z","updated_at":"2019-11-05T14:03:54.813Z"}, {"id":4,"name":"item4","price":400,"user_id":2,"created_at":"2019-11-05T14:03:54.822Z","updated_at":"2019-11-05T14:03:54.822Z"} ] }, { "id":3,"name":"User3","created_at":"2019-11-05T14:03:54.831Z","updated_at":"2019-11-05T14:03:54.831Z", "items":[ {"id":5,"name":"item5","price":500,"user_id":3,"created_at":"2019-11-05T14:03:54.839Z","updated_at":"2019-11-05T14:03:54.839Z"}, {"id":6,"name":"item6","price":600,"user_id":3,"created_at":"2019-11-05T14:03:54.848Z","updated_at":"2019-11-05T14:03:54.848Z"} ] } ]
|
構造化された形の json 形式で出力できました。
関連モデルを json 出力 partial を使わない jbuilder 編
調べていると「partial 遅い」が検索に良く引っかかります。
だそうなので、partial を使わない書き方も確認しておきます。
app/controllers/users_controller.rb1 2 3 4 5 6 7
| class UsersController < ApplicationController
def index @users = User.all end
|
app/views/users/index.json.jbuilder1 2 3 4 5 6
| json.array! @users do |user| json.extract! user, :id, :name, :created_at, :updated_at json.items do json.array! user.items end end
|
ブラウザで/users.json にアクセスすると、以下のレスポンスになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| [ { "id":1,"name":"User1","created_at":"2019-11-05T14:03:54.753Z","updated_at":"2019-11-05T14:03:54.753Z", "items":[ {"id":1,"name":"item1","price":100,"user_id":1,"created_at":"2019-11-05T14:03:54.786Z","updated_at":"2019-11-05T14:03:54.786Z"}, {"id":2,"name":"item2","price":200,"user_id":1,"created_at":"2019-11-05T14:03:54.795Z","updated_at":"2019-11-05T14:03:54.795Z"} ] }, { "id":2,"name":"User2","created_at":"2019-11-05T14:03:54.805Z","updated_at":"2019-11-05T14:03:54.805Z", "items":[ {"id":3,"name":"item3","price":300,"user_id":2,"created_at":"2019-11-05T14:03:54.813Z","updated_at":"2019-11-05T14:03:54.813Z"}, {"id":4,"name":"item4","price":400,"user_id":2,"created_at":"2019-11-05T14:03:54.822Z","updated_at":"2019-11-05T14:03:54.822Z"} ] }, { "id":3,"name":"User3","created_at":"2019-11-05T14:03:54.831Z","updated_at":"2019-11-05T14:03:54.831Z", "items":[ {"id":5,"name":"item5","price":500,"user_id":3,"created_at":"2019-11-05T14:03:54.839Z","updated_at":"2019-11-05T14:03:54.839Z"}, {"id":6,"name":"item6","price":600,"user_id":3,"created_at":"2019-11-05T14:03:54.848Z","updated_at":"2019-11-05T14:03:54.848Z"} ] } ]
|
構造化された形の json 形式の出力を partial を使わずに作ることができました。
jbuilder も絡めて、Ruby on Rails での json 出力について確認できました。
Ruby on Rails の学習を進めていますが、改めて初期に書いた記事を見ると、理解が浅いゆえに間違っている部分あるようです。
復習の意味も込めつつ textlint と仲良くしながら追々直していきたいです。
ではでは。