Ruby on Railsで多対多の実現
昨日からRailsを使ってちょっとしたプロジェクト支援ツールを作っています。
プロジェクトにはメンバーが所属することになりますが、その関連は多対多です。今日はこれを実現してみたい。
モデル作成、テーブル作成
必要なのはProjectクラスとMemberクラス。Projectクラスは既に昨日作ってあるので、今日はまずMemberクラスを作成します。
ruby script/generator scaffold member name:string password:string mail_address:string
で、お決まりパターンのDBの作成。
rake db:migrate
多対多の関連を持つには、通常DB上ではProjectsテーブルとMembersテーブルをつなぐマッピングテーブルが必要となりますがRailsであっても同様のようです。以下をMySQLにアクセスし、実行します。テーブルの命名は以下のようにしないといけません。ただprojects_membersだとうまくいきませんでした。アルファベット順ってことかな。
mysql> create table members_projects(project_id integer, member_id integer);
DBの準備が出来たらクラスを編集します。まずはProjectクラスから。SubProjectsクラスとの1対多関連に続き多対多の関連を追加。
class Project < ActiveRecord::Base has_many :sub_projects has_and_belongs_to_many :members end
次にMemberクラス。
class Member < ActiveRecord::Base has_and_belongs_to_many :projects end
テスト
では早速多対多が実現できたかどうかをテストしてみます。
まずテストデータから。以下はmembers.yml。
one: id: 1 name: taro password: taro mail_address: taro@hogehoge.com two: id: 2 name: hanako password: hanako mail_address: hanako@hogehoge.com
次にmembers_projects.yml。このファイルは自動生成されないので、自分で作成します。
one: member_id: 1 project_id: 1 two: member_id: 2 project_id: 1 three: member_id: 1 project_id: 2
project.ymlは昨日と同じ。一応載せておきます。
one: id: 1 name: MyString1 description: MyText from: 2008-06-15 to: 2008-06-15 two: id: 2 name: MyString2 description: MyText from: 2008-06-15 to: 2008-06-15
で、テストケースです。昨日のproject_test.rbに追加してみました。
def test_meny_to_many p1 = Project.find(1) assert_equal(2, p1.members.size) p2 = Project.find(2) assert_equal(1, p2.members.size) assert_equal("taro", p2.members[0].name) end
一応member_test.rbも。こちらはメンバー自分自身が所属しているプロジェクトの数を確認しています。
class MemberTest < ActiveSupport::TestCase def test_many_to_many taro = Member.find(1) assert_equal(2, taro.projects.size) hanako = Member.find(2) assert_equal(1, hanako.projects.size) assert_equal("MyString1", hanako.projects[0].name) end end
テストの実行。
rake
うまくいきました♪
保存
ではscript/consoleを使って実際に保存してみます。
ruby script/console
とやって、プログラムを書いていきます。
p1 = Project.create(:name=>"sample project") taro = Member.create(:name=>"taro") hanako = Member.create(:name=>"hanako") p1.members << taro << hanako
createメソッドはnewとsaveが同時に行われます。つまり生成と同時に永続化するってことです。この状態で一気にtaroとhanakoを追加すると特にsaveなどせずともこの時点でmembers_projectsテーブルにデータが2件追加されていました。
できたー♪