【Ruby on Rails】Backbone.jsにinclude先の情報を渡す方法と注意点
次のような中間テーブルを挟むhas_manyなDB構造の場合
users table
id | |
1 | test1@gmail.com |
2 | test2@gmail.com |
3 | test3@gmail.com |
4 | test4@gmail.com |
5 | test5@gmail.com |
users_services table
id | user_id | service_id | role |
1 | 3 | 2 | role1 |
2 | 2 | 1 | role2 |
3 | 3 | 3 | role2 |
4 | 1 | 1 | role2 |
5 | 1 | 2 | role1 |
services table
id | name |
1 | service1 |
2 | service2 |
3 | service3 |
railsのコントローラで以下のように書いてもinclude先の情報はbackboneに渡されません。
index_controller.rb
def index @users = User.find( :all, :include => :services, ) end
index.html.erb
<script type="text/javascript"> $(function() { window.router = new Blog.Routers.UsersRouter({users: <%= @users.to_json.html_safe -%>}); Backbone.history.start(); }); </script>
to_json時にincludeしなければなりません。
index_controller.rb
def index @users = User.find( :all, :include => :services, ).to_json( :include => :services, ).html_safe end
index.html.erb
<script type="text/javascript"> $(function() { window.router = new Blog.Routers.UsersRouter({users: <%= @users -%>}); Backbone.history.start(); }); </script>
中間テーブルのデータをとってくるときの注意点
以下のように中間テーブルのデータを取ってこようとすると
User.find( :all, :include => :services, :conditions => ["services.id = ?", 1], )).to_json( :only => [:email,:id], :include => {:users_services => {:only => [:service_id, :role]}}, )
次のようなjsonがかえってきます。
'[{"email":"test2@gmail.com","id":2,"users_services":[{"role":"role2","service_id":1}]},{"email":"test1@gmail.com","id":1,"users_services":[{"role":"role2","service_id":1},{"role":"role1","service_id":2}]]'
最後を見てもらえればわかる通りconditionsでservice_idが1のものを指定しているのに2のものがくっついてきています。
to_jsonのinclude時のsql文を見てみると
SELECT `users_services`.* FROM `users_services` WHERE `users_services`.`user_id` = 1 SELECT `users_services`.* FROM `users_services` WHERE `users_services`.`user_id` = 2
なぜかuser_idで絞ってます。
この問題を回避するにはto_json時にmethodsを定義します。
index_controller.rb
def index @users = User.find( :all, )).to_json( :only => [:email,:id], :methods => :fetch_service_id_one ) end
user.rb
def fetch_service_id_one users_services.where("service_id = ?", 1).select("service_id, role") end
SELECT service_id, role FROM `users_services` WHERE `users_services`.`user_id` = 1 AND (service_id = 1) SELECT service_id, role FROM `users_services` WHERE `users_services`.`user_id` = 2 AND (service_id = 1)
返ってくるjson
'[{"email":"test2@gmail.com","id":2,"fetch_service_id_one":[{"role":"role2","service_id":1}]},{"email":"test1@gmail.com","id":1,"fetch_service_id_one":[{"role":"role2","service_id":1}]]'
service_idが1のものだけ返ってくるようになりました。
もしこうしたほうが楽にできる、ここ間違ってるなどあれば教えてくださいm(_ _)m