Rails3からhas_and_belongs_to_manyより柔軟なhas_many thorughというRelationshipができたので試す。ブログの記事を登録する際に、セレクトボックスで複数のカテゴリを選択する場合を想定している。コントローラーやviewについて記述が無い箇所は、scaffoldで生成したコードと同等のもので動くと思う。
まずはmigrationを使ってテーブルを作る。
# ブログ記事
class Posts < ActiveRecord::Migration
def change
create_Table :posts do |t|
t.string :title
t.text :body
t.timestamps
end
end
end
# カテゴリ
class Categories < ActiveRecord::Migration
def change
create_Table :categories do |t|
t.string :name
t.timestamps
end
end
end
# ブログ記事とタグを繋ぐ中間テーブル
class Categorizations < ActiveRecord::Migration
def change
create_Table :categorizations do |t|
t.references :post, null: false
t.references :category, null: false
t.timestamps
end
end
end
中間テーブルのカラム名を複数形にしてしまい少しハマッたので注意。次にモデルを定義する。
# ブログ記事のモデル class Post < ActiveRecord::Base has_many :categorizations, dependent: :destroy has_many :categories, through: :categorizations end # カテゴリのモデル class Category < ActiveRecord::Base has_many :categorizations, dependent: :destroy has_many :posts, through: :categorizations end # 中間テーブルのモデル class Categorization < ActiveRecord::Base belongs_to :post belongs_to :category end
最後に、ブログ記事の新規登録画面のviewを少し書き換える。
<%= form_for @post do |f| %>
<%= render '/shared/validation_messages', :model => @product %>
<div class="field">
<%= f.label :name %><br/>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :body %><br/>
<%= f.text_area :body %>
</div>
<div class="field">
<%= f.label :category_ids %><br/>
<%= f.select :category_ids, Category.all.map {|c| [c.name, c.id]}, {}, multiple: true %>
</div>
</div>
<div class="actions">
<%= f.submit 'Launch this product' %>
</div>
</div>
<% end %>
これを使うまで知らなかったんだけど、フォームの項目名を複数系にすることでmultipleな入力も受け付けてくれるようなのですごく簡単にこういったことができる。