Ruby on Railsで複合キーを扱う(2)

2012/03/26

前回は、Gemライブラリcomposite_primary_keysを用いるとRailsアプリケーションにおいて複合キーでレコードを検索したり、複合キーを持つモデル同士を関連づけできることを確かめました。

今回はルーティング編です。

コントローラの生成・実装からルーティングの設定まで

% rails g controller departments index
% rails g controller products index show

app/controllers/departments_controller.rb を修正。

class DepartmentsController < ApplicationController
  def index
    @departments = Department.order("code, seq_number")
  end
end

app/controllers/products_controller.rb を修正。

class ProductsController < ApplicationController
  def index
    @department = Department.find(params[:department_id])
    @products = @department.products.order("products.code, products.model_number")
  end
  
  def show
    @department = Department.find(params[:department_id])
    @product = @department.products.find(params[:id])
  end
end

config/routes.rb を修正。

Synthetos::Application.routes.draw do
  resources :departments do
    resources :products
  end
end

ビューの実装

app/views/departments/index.html.erb を修正。

<h1>Departments#index</h1>

<ul>
  <% @departments.each do |d| %>
    <li>
      <%= d.name %>: <%= link_to "Products", [ d, :products ] %>
    </li>
  <% end %>
</ul>

app/views/products/index.html.erb を修正。

<h1>Products#index</h1>

<ul>
  <% @products.each do |p| %>
    <li>
      <%= link_to p.name, [ @department, p ] %>
    </li>
  <% end %>
</ul>

app/views/products/show.html.erb を修正。

<h1>Products#show</h1>

<ul>
  <li>Name: <%= @product.name %></li>
  <li>Code: <%= @product.code %></li>
  <li>Model number: <%= @product.model_number %></li>
  <li>Description: <%= @product.description %></li>
</ul>

シードデータの投入

config/routes.rb を修正。

%w(robot automobile ship).each do |code|
  Department.create!(
    code: code,
    seq_number: 1,
    name: code.capitalize,
    established_on: 2.years.ago
  )
end

%w(alpha bravo).each do |code|
  Product.create!(
    code: code,
    model_number: 2012,
    department_code: "robot",
    department_seq_number: 1,
    name: code.capitalize,
    description: ""
  )
end
% rake db:reset

モデルの修正

Gemライブラリcomposite_primary_keysの説明によれば、["robot", 1] という複合キーを持つオブジェクト@departmentに対して、to_paramメソッドを呼ぶと "robot,1" という文字列が返ってくるべきなのですが、実際には "robot-1" という文字列が返ってきます。これではいろいろとうまく動かないので、モデルに少し修正を加えます。

たぶん composite_primary_keys がRails 3に完全に対応し切れていないためと思われます。作者に連絡したので、そのうちに直るでしょう。

app/model/department.rb を次のように修正します。

class Department < ActiveRecord::Base
  self.primary_keys = :code, :seq_number
  has_many :products, foreign_key: [ :department_code, :department_seq_number ]
  
  def to_param
    persisted? ? to_key.join(',') : nil
  end
end

同様に、app/model/product.rb を次のように修正します。

class Product < ActiveRecord::Base
  self.primary_keys = :code, :model_number
  belongs_to :department,
    foreign_key: [ :department_code, :department_seq_number ],
    primary_key: [ :code, :seq_number ]

  def to_param
    persisted? ? to_key.join(',') : nil
  end
end

ブラウザで動作確認

% rails s

ブラウザで http://localhost:3000/departments を開くと次のような画面になります。

composite_primary_keys_1_1

「Robot」の右の「Products」リンクをクリックすると次のような画面になります。URLに注目してください。

composite_primary_keys_1_2

そして、「Alpha」リンクをクリックすると次のような画面になります。

composite_primary_keys_1_3

ちょっとしたバグ(?)が見つかりましたが、ルーティングはうまく動いているようですね。ただし、URLにコンマ(,)が含まれる点はちょっと気になります。

次回は、複合キーの一つが文字列や数値ではなく、「期間」になっている場合について考えます。