タイトルの通り、Rails で複数のモデルを横断したリストを作る場合のやり方を調べたらできたので、メモ的に残しておく。
目次
参考
準備 とりあえず、rails の環境を用意します。 モデルは、scaffold を使用して user と food を作ります。 この user と food は持っている要素が異なっていることとします。
user は、名前(name)と年齢(age)
food は、名前(name)と価格(price)
以下のコマンドを実行します。
1 2 3 4 5 6 7 bundle init bundle install --path vender/bundle bundle exec rails new . bundle exec rails db:create bundle exec rails g scaffold user name:string age:integer bundle exec rails g scaffold food name:string price:integer bundle exec rails db:migrate
モデル横断リスト実装 データ準備 bundle exec rails s
で起動し、localhost:3000/users
localhost:3000/foods
それぞれにアクセスして、一旦データを作成します。 user は 1 件、food は 3 件登録します。
一旦確認 そもそも、どうやってモデルを横断したリストを作れるのか。 コンソールで確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 irb(main):001:0> @users =User.all User Load (1.6ms) SELECT "users" .* FROM "users" LIMIT ? [["LIMIT" , 11]] => irb(main):002:0> @foods=Food.all Food Load (0.2ms) SELECT "foods" .* FROM "foods" LIMIT ? [["LIMIT" , 11]] => irb(main):003:0> @users.length User Load (0.2ms) SELECT "users" .* FROM "users" => 1 irb(main):004:0> @foods.length Food Load (0.2ms) SELECT "foods" .* FROM "foods" => 3 irb(main):005:0> @lists=@users +@foods => [ irb(main):006:0> @lists.length => 4 irb(main):007:0> @lists[0].class => User(id : integer , name: string, age: integer , created_at: datetime, updated_at: datetime) irb(main):008:0> @lists[1].class => Food(id : integer , name: string, price: integer , created_at: datetime, updated_at: datetime) irb(main):009:0> @lists[2].class => Food(id : integer , name: string, price: integer , created_at: datetime, updated_at: datetime) irb(main):010:0> @lists[3].class => Food(id : integer , name: string, price: integer , created_at: datetime, updated_at: datetime) irb(main):011:0> @lists_f2=@lists.first(2) => [ irb(main):012:0> @lists_f2.length => 2
というわけで、それぞれの Active Record 取得結果は+
で連結できた。 こちらを踏まえてコントローラとビュー、ルーティングを作成する。
実装 以下のファイルを作成する。config\routes.rb
config\routes.rb 1 2 3 4 5 6 Rails .application.routes.draw do resources :foods resources :users get '/mixlists' , to: 'mix_lists#index' #<=ここを手で追加 # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end
app\controllers\mix_lists_controller.rb
app\controllers\mix_lists_controller.rb 1 2 3 4 5 6 7 8 9 10 11 class MixListsController < ApplicationController def index foods = Food .all users = User .all lists = foods + users @lists_sorted = lists.sort{|a,b | a.created_at <=> b.created_at} end end
app\views\mix_lists\index.html.erb
app\views\mix_lists\index.html.erb 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <table > <thead > <tr > <th > Type</th > <th > Name</th > <th > age or price</th > <th > created_at</th > </tr > </thead > <tbody > <% @lists_sorted .each do |c | %> <tr > <td > <%= c.class %></td > <td > <%= c.name %></td > <% if c.class == User %> <td > <%= c.age %></td > <td > <%= link_to 'Edit' , edit_user_path(c) %></td > <% elsif c.class == Food %> <td > <%= c.price %></td > <td > <%= link_to 'Edit' , edit_food_path(c) %></td > <% end %> </td > <td > <%= c.created_at %></td > </tr > <% end %> </tbody > </table >
確認 bundle exec rails s
で起動し、localhost:3000/mixlists
にアクセスすると、以下の画面になります。
User と、Food を 1 つのリストで表示できました。 1 つのリストにしながらも、クラスを判定して user 向け food 向けそれぞれのリンクを作成することもできました。
今回はモデル横断したリストを作成しました。 最初は、「テーブルを join するとか、何かしなければいけないのでは?」などと考えて深いふっかーい泥沼にはまりましたが、問題は単純でした。 user と food を並べる状況はわかりませんが、例えば各モデルへの変更履歴を別途テーブルに作りすべての操作履歴を俯瞰で見たい場合は、利用価値がありそうです。
ではでは。