テーブル間のリレーションについて
ユーザ情報テーブルと、ユーザが持ってる所持品のテーブル的なものがあるとして。
面倒なのでスゲー適当だけど以下のような定義で作った。
rails g scaffold user name:string item_id:integer
rails g scaffold item name:string
item_id は items テーブルの id が入るってことね。
- users
- name:string
- item_id:integer
- items
- name:string
これで、ユーザはitem_idに items テーブルのidを一つもつ感じ。
これを json にして出力したいぜ
例えば以下のようなデータがあるとして
- users
- id => 1
- name => aaaa
- item_id => 1
- items
- id => 1
- name => item1
irbで確認するとこんな感じ。
ruby-1.9.2-head > User.find_by_id(1)
=> #
ruby-1.9.2-head > User.find_by_id(1).item
=> #
うん、予定通りのデータが入ってるね。じゃあ、これを json で出力してみましょう。
ruby-1.9.2-head > User.find_by_id(1).to_json
{ user: { created_at: 2010-09-13T21:18:44Z (string) ,id: 1 (number) ,item_id: 1 (number) ,name: aaaa (string) ,updated_at: 2010-09-13T21:18:44Z (string) } }
たしかに json 形式なんだけど、item の値が入っていない。あんまりだ。
ちなみに、json の整形は JSON整形 を使った。
to_json にはオプションがいろいろある
テストデータがアレな気がするけど、オプションについて詳しく載っていてとても良い。
つまり :include しろってことだってばよ
ruby-1.9.2-head > puts User.find_by_id(1).to_json(:include => :item)
{ user: { created_at: 2010-09-13T21:18:44Z (string) ,id: 1 (number) ,item_id: 1 (number) ,name: aaaa (string) ,updated_at: 2010-09-13T21:18:44Z (string) ,item: { created_at: 2010-09-13T21:17:10Z (string) ,id: 1 (number) ,name: item1 (string) ,updated_at: 2010-09-13T21:17:10Z (string) } } }
やった、itemのデータも引っ張ってこれた!
ただ、これを出力データとするにはちょっと冗長すぎる。updated_at とか必要かっつーと入らなかったりする。
:only や :except で出力を制限できる
例えばこんな感じだ
User.find(:all)[0].to_json({:include => {:item => {:only => [:id,:name]}}, :only => [:name]})
{ user: { name: aaaa (string) ,item: { id: 1 (number) ,name: item1 (string) } } }
これ、only が items にも users にも指定してあって、ちょっと重複した感じがしてイヤなのだけど、上位(この場合 users)で指定した場合、その値が子の要素にも only の設定が伝播してしまう。
今回の場合、両方とも name しかないからあまり実害はないけれど、実際の場合はそんな訳はないので、個別に出力する属性を指定する感じの方が良いと思う。
ちなみに、to_xml でも同様です。
じゃあ、respond_with の場合ってどうなるのよ
こんな感じで実装できた。
class UsersController < ApplicationController respond_to :html, :xml, :json # GET /users # GET /users.xml def index @users = User.all respond_with(@users, {:include => {:item => {:only => [:id,:name]}}, :only => [:name]}) end (略) end
従来の respond_to の場合はこんな感じだろうか(面倒なので、とりあえず json の場合のみ)。
respond_to do |format| format.html # index.html.erb format.xml { render :xml => @users } format.json { render :js => @users.to_json({:include => {:item => {:only => [:id,:name]}}, :only => [:name]}) } end
こう見ると respond_with の cool 感が際立ってやべぇすな。
*1:意味的にあったるかは微妙な気がする。itemの所属するって変じゃね!?