【Rails入門】has_many、belongs_toの使い方まとめ

こんにちは! フリーエンジニアの長瀬です。

has_manybelongs_toってなんだか難しいと思ってませんか?

関連付けをすれば、データを効率良く管理できるようになります。

この記事では、has_many、belongs_toの使い方について

・has_manyとは

という基本的な内容から、

・throughを用いてより複雑な関連付けを行うには
・関連付けされたモデルのインスタンスを生成する方法

といった応用的な内容についても解説していきます。

目次

has_manyとは

has_manyはテーブル同士を関連付けるためにあります。

関連付けをすることによって、データをまとめて扱えるようになるので、より効率的にデータベースを操作できます。

関連付け(アソシエーション)とは

関連付けとは、テーブル同士に関係があることを明示的に表現することです。

どういった関係があるのかということをあらかじめ教えてあげることで、ある操作をする場合により簡潔に表現できるようになります。

たとえば、以下の図のような関連づけられたテーブルがあったとします。

Screen Shot 2560-07-06 at 11.30.36

佐藤さんという人が猫のタマと犬のポチを飼っているとします。

佐藤さんはOwner(所有者)テーブル、タマとポチはPet(ペット)テーブルにそれぞれ保存されています。

この図のように、テーブルが関連づけられている場合、もしも佐藤さんがタマとポチを手放したときに、佐藤さんが飼っているペットをすべて削除するという命令が直接できるようになります。

また、新たにペットを追加するときに自動的に佐藤さんに飼われているペットしとて追加することもできます。

このような関係を1対多の関係と呼びます。
この場合、佐藤さんが『1』でタマとポチは『多』です。

関連付けには1対多の他に多対多もあります。
上記の例はowner_id1つだけが関連付けられていたので『1』でしたが、これが複数になってくると多対多の関係になります。

pet&keeper&seller

このように、keeper(飼い主)に加えて、seller(販売員)を加えてみました。
Petテーブルに2つ以上の関連付けがされているので、多対多の関係と呼ばれます。

こうすることで、Pet(ペット)を登録した際に、飼い主と販売員の2人を関連づけることができます。

関連付けをすれば、

・データを削除するときに、関連づけられたものをすべてまとめて削除する場合
・従属テーブルに値を追加するときに、自動的に関連付けるをする場合

より簡潔に表現できるようになります。

has_manyの基本的な実装方法

それでは,実際にhas_manyを使って、関連付けをしてみましょう。

Owner(所有者)テーブルとCastle(城)テーブルを作成して、関連付けをします。

まずはモデルとカラムを指定するためにコマンドプロンプトに以下のコードを入力してください。

[モデルとカラムを作成するサンプルコード]

rails g model owner name:string
rails g model castle castle:string owner_id:integer

[実行結果]

 create    db/migrate/20170705083951_create_owners.rb
      create    app/models/owner.rb
      invoke    test_unit
      create      test/models/owner_test.rb
      create      test/fixtures/owners.yml

create    db/migrate/20170705083952_create_castles.rb
      create    app/models/castle.rb
      invoke    test_unit
      create      test/models/castle_test.rb
      create      test/fixtures/castles.yml

これで、それぞれモデルとマイグレーションファイルが作成されました。

データベースに反映するためにコマンドプロンプトに以下のコードを入力してください。

rake db:migrate

これで、指定したカラムの内容がデータベースに反映されました。

それでは、カラムにデータを追加していきます。

rails consoleでコンソールを起動した後、以下のコードを入力してください。

[モデルにデータを追加するサンプルコード]

Castle.create(castle:"大阪城",owner_id:2)
Castle.create(castle:"岐阜城",owner_id:3)
Castle.create(castle:"安土城",owner_id:3)
Castle.create(castle:"江戸城",owner_id:1)
Castle.create(castle:"首里城",owner_id:4)
Owner.create(name:"徳川家康")
Owner.create(name:"豊臣秀吉")
Owner.create(name:"織田信長")

これで、各テーブルのカラムにデータが追加されました。

それでは、has_manyを使っていきましょう。

今回は、OwnerがCastle(城)を所有しているという関係を表したいので、app/models/owner.rbに以下のコードを入力してください。

[app/models/owner.rbに入力する内容]

has_many :castles, dependent: :destroy

これで、OwnerがCastlesを所有しているという関係が設定できました。

:castlesが複数形であることに注意してください。

Ownerが所有している城が1つとは限らないので複数形になっています。

dependent: :destroyを追加しておくと、たとえば、織田信長が所有する城をすべて削除するということを簡潔に命令できるようになります。

belongs_toとは

続いて、今度はbelongs_toを使って、Castles(城)はOwnerによって所有されているという関係を追加していきましょう。

app/models/castle.rbに以下のコードを入力してください。

[app/models/castle.rbに入力する内容]

belongs_to :owner

これで,Castles(城)はOwnerによって所有されているという関係を表現できました。

関連名をカスタマイズする方法

has_manyやbelongs_toの後に続く関連名(モデル名を小文字にしたもの 例: owner)はカスタマイズすることが可能です。

ただし、その際は外部キーをforeign_keyオプションを用いて明示的に指定してあげる必要があります。

app/models/castle.rbに以下のコードを入力して、書き換えてください。

[app/models/castle.rbに入力する内容]

belongs_to :holder ,class_name: 'Owner',foreign_key: 'owner_id'

belongs_toの部分に新しい関連名を入力します。

そして、class_nameに参照先のテーブルの名前を指定し、foreign_keyには(モデル名)+_idを入力します。

こうすることで、holderという名前でOwnerテーブルにアクセスできるようになります。

実際にやってみましょう。

とその前にデータをコンソールに表形式で出力するためにHirbというgemを追加します。
Hirbのインストール方法については以下の記事内に説明がありますので、参考にしてください。

rails consoleでコンソールを起動した後、以下のコードを入力してください。

[新しく定義した名前でOwnerテーブルにアクセスするサンプルコード]

Hirb.enable #表形式で出力
Castle.find(2).holder

[実行結果]

+----+----------+---------------------------+---------------------------+
| id | name     | created_at                | updated_at                |
+----+----------+---------------------------+---------------------------+
| 3  | 織田信長 | 2017-07-05 17:40:48 +0900 | 2017-07-05 17:40:48 +0900 |
+----+----------+---------------------------+---------------------------+
1 row in set

このようにholderでOwnerテーブルにアクセスできました。

referencesを使ってidを関連付ける方法

さきほど、

rails g model castle castle:string owner_id:integer

上記のコードで、castleモデルを追加した際にowner_id:integerと書いて、明示的に関連づけられたidを設定しましたが、実はより簡単に表現できます。

[referencesを使って、関連付けを行うサンプルコード]

rails g model castle castle:string owner:references

このようにreferencesを使うことで、自動的にowner_idのカラムがinteger型で作成されます。

また、referencesを使った場合はapp/model/castle.rb にbelongs_to :ownerが自動で追加されます。

ですので、使えるなら、いちいち手動で設定するよりもこちらのコマンドを使うよにしてください。

throughを用いてより複雑な関連付けを行うには

has_many :throughの使い方

has_many :throughを使えば、多対多の関係を表現できます。

それでは、先ほど説明したような多対多の関係をhas_many :throughを使って表現してみましょう。

重複しないように、Ownerテーブルの代わりにKeeperテーブルを使っています。

モデルを作成するために、コマンドプロンプトに以下のコードを入力してください。

[各モデルを作成するためのサンプルコード]

rails g model keeper name:string
rails g model seller name:string
rails g model pet name:string keeper:references seller:references
rake db:migrate

これで、モデルを作成して、データベースにマイグレーションファイルの内容を反映できました。

次に今回使うデータを入れるので、rails consoleでコンソールを起動した後、以下のコードを入力してください。

[今回使うデータを入れる]

Keeper.create(name:"佐々木一郎")
Keeper.create(name:"長瀬来")
Keeper.create(name:"北川俊介")
Seller.create(name:"畠山千鶴")
Seller.create(name:"宮山啓介")
Seller.create(name:"木内宗太郎")
Pet.create(name:"プラナリア",keeper_id:1,seller_id:3)
Pet.create(name:"文鳥",keeper_id:3,seller_id:2)
Pet.create(name:"カブトムシ",keeper_id:3,seller_id:1)
Pet.create(name:"猫",keeper_id:2,seller_id:2)
Pet.create(name:"犬",keeper_id:2,seller_id:1)
Pet.create(name:"クモ",keeper_id:1,seller_id:2)

これで、各テーブルに必要なデータが入りました。

それでは、関連付けを行っていきましょう。
app/models/keeper.rbを開いて、以下のコードを入力してください。

[app/models/keeper.rbに入力する内容]

has_many :pets
   has_many :sellers, through: :pets

次に、app/models/seller.rbにも同様に

[app/models/seller.rbに入力する内容]

has_many :pets
  has_many :keepers, through: :pets

これで、関連付けが完了しました。

app/models/pet.rbにはreferencesを使ったので、自動的にそれぞれにbelongs_toが追加されています。

関連付けされたモデルのインスタンスを生成する方法

それでは今回関連づけたモデルを使って、インスタンスを作成してみましょう。

さきほど作成したcastle、ownerモデルを使います。

インスタンスを作成するにはnewbuildがあります。

railsがバージョンアップする前はnewでは、関連付けられたidが自動で入力されないという違いがありましたが、現在ではどちらも同じ動作をします。

なので、どちらを使っても変わりありません。

それでは、newとbuildを使ってインスタンスを作成してみましょう。

rails consoleでコンソールを起動した後、以下のコードを入力してください。

[newを使ってインスタンスを作成するサンプルコード]

Hirb.enable #表形式で出力
owner = Owner.find(3) #織田信長のidを取得
owner.castles.new#インスタンスを作成

[実行結果]

+----+--------+----------+------------+------------+
| id | castle | owner_id | created_at | updated_at |
+----+--------+----------+------------+------------+
|    |        | 3        |            |            |
+----+--------+----------+------------+------------+
1 row in set

[buildを使ってインスタンスを作成するサンプルコード]

Hirb.enable #表形式で出力
owner = Owner.find(3) #織田信長のidを取得
owner.castles.build#インスタンスを作成

[実行結果]

+----+--------+----------+------------+------------+
| id | castle | owner_id | created_at | updated_at |
+----+--------+----------+------------+------------+
|    |        | 3        |            |            |
+----+--------+----------+------------+------------+
1 row in set

このように、どちらも同じ実行結果になることが確認できました。

次は今回作成したモデルを使って、実際にデータを追加してみましょう。

まずは1対多のCastle、Ownerモデルからです。

rails consoleでコンソールを起動した後、以下のコードを入力してください。

[has_many,belongs toを使ったモデルでインスタンスを作成するサンプルコード]

owner = Owner.find(2) #豊臣秀吉を取得
owner.castles.create(castle:"伏見城”)
Hirb.enable #表形式で出力
Castle.all

[実行結果]

+----+--------+----------+---------------------------+---------------------------+
| id | castle | owner_id | created_at                | updated_at                |
+----+--------+----------+---------------------------+---------------------------+
| 1  | 大阪城 | 2        | 2017-07-05 17:41:28 +0900 | 2017-07-05 17:41:28 +0900 |
| 2  | 安土城 | 3        | 2017-07-05 17:41:28 +0900 | 2017-07-05 17:41:28 +0900 |
| 3  | 江戸城 | 1        | 2017-07-05 17:41:28 +0900 | 2017-07-05 17:41:28 +0900 |
| 4  | 伏見城 | 2        | 2017-07-06 10:13:22 +0900 | 2017-07-06 10:13:22 +0900 |
+----+--------+----------+---------------------------+---------------------------+
4 rows in set

このように、自動的にowner_idに2が追加されたのが確認できます。

次にhas_many :thoughを使った多対多のモデルにデータを追加してみましょう。

rails consoleでコンソールを起動した後、以下のコードを入力してください。

[has_many :throughを使ったモデルでインスタンスを作成するサンプルコード]

keeper = Keeper.create(name:"田中太郎") #新しい飼い主
seller = Seller.create(name:"立川絢香" ) #販売した人
keeper.pets.create(name:"ウーパールーパー",seller: seller) #ウーパールーパーを購入
Hirb.enable #表形式で出力
Pet.all

[実行結果]

+----+------------------+-----------+-----------+---------------------------+---------------------------+
| id | name             | keeper_id | seller_id | created_at                | updated_at                |
+----+------------------+-----------+-----------+---------------------------+---------------------------+
| 1  | プラナリア       | 1         | 3         | 2017-07-06 10:47:40 +0900 | 2017-07-06 10:47:40 +0900 |
| 2  | 文鳥             | 3         | 2         | 2017-07-06 10:47:40 +0900 | 2017-07-06 10:47:40 +0900 |
| 3  | カブトムシ       | 3         | 1         | 2017-07-06 10:47:40 +0900 | 2017-07-06 10:47:40 +0900 |
| 4  | 猫               | 2         | 2         | 2017-07-06 10:47:40 +0900 | 2017-07-06 10:47:40 +0900 |
| 5  | 犬               | 2         | 1         | 2017-07-06 10:47:40 +0900 | 2017-07-06 10:47:40 +0900 |
| 6  | クモ             | 1         | 2         | 2017-07-06 10:47:41 +0900 | 2017-07-06 10:47:41 +0900 |
| 7  | ウーパールーパー | 4         | 4         | 2017-07-06 10:53:40 +0900 | 2017-07-06 10:53:40 +0900 |
+----+------------------+-----------+-----------+---------------------------+---------------------------+
7 rows in set

このように、keeper_id、seller_idにそれぞれのidが入力されたのが確認できます。

Railsを独学で頑張っているけど先が見えない方のために

独学でRailsを頑張っているわけだけど、先が見えない・・・

そんな方、実はいらっしゃるのではないでしょうか?

いや、おれは違うぞ。先のキャリアもしっかり見えてるし、独学の成果も出てきている

そんな方であれば、これから先の話は必要ないでしょう。そっとページの閉じるボタンを押しましょう。

しかし、「先が見えない」と心の底では勘付いているそこの奥さん。この先を読み進めて、一緒に課題を深堀りしていきましょう。

なぜ「先が見えない」という不安や悩みを抱えてしまうのか

さて、「一寸先は闇だ・・・」とお悩みを抱えている方に、なぜ独学でRailsを勉強しているにもかかわらず、そのような現状を抱えてしまうのか、一緒に考えていきましょう。

先が見えない現状を踏まえ、課題として考えられるものは以下のどれかに該当するでしょう。

  • プログラミングの上達が見えない
  • プログラミングを継続できない気がする
  • プログラミングスキルを習得した姿がイメージできない
  • プログラミングスキルを活かした仕事を獲得するイメージができない

これらのどれかに該当することによって、「なんとなくプログラミング学習をしている」という状態になってしまいます。

これらの要因は、三日坊主になる理論と同じなんですが、「プログラミング学習をしなきゃ」とプログラミング学習を頑張ってしまっている状態になってしまっています。

受験勉強をやった経験のある方なら頭がもげるほどに首を縦に振ってしまう方も多いのですが、「今日も5時間勉強するぞ」や「今日はこの章を終わらすぞ」というように、学習を進めることに意識が行き過ぎてしまうと、ある程度学習を継続した後に「先が見えない・・・」となってしまいます。

未来に光を当て、プログラミング学習を「成果が出るもの」にするために

先ほど、「なぜ先が見えないという悩みや不安を抱えてしまうのか」という疑問に対しての答えを示していきました。

これらの課題というのは、独学をしていれば9割の方がぶつかってしまう壁だそうで、いわば、あるあるの現象なのです。

独学をしていて、「なんか前に進めていないぞ」と感じるのはこのせいなんですね。甘く見がちですが、非常にやっかい。

これがさらにやっかいさを極めているのは、上記に挙げた課題のほとんどが、1人で解決できないものばかりだからです。

実は、これらのほとんどが経験者に助けてもらいながら解決しないと、すぐに違う方向へと流れてしまいます。

そう言い切れるのは、以前の私もそうだったからです。

エンジニアやプログラマー関連のキャリアに詳しい方や現役のエンジニアに相談しながら修正を加え、学習を実践して今があります。

そうは言っても、周りにそういう人がいないし・・・

という方もいるでしょう。そういう時にこそ、プログラミングスクールの無料カウンセリングを利用するのです。

営業をかけられて時間の無駄に終わるでしょ?

という考えに辿りついてしまいますよね。結論から言うと弊社では、そういった強引な営業等を行うことはありませんので、安心して受講できます。

「プログラミング学習の先ある未来」を光で明るく照らすには、弊社の無料カウンセリングがぴったりだと断言できます。それくらい無料カウンセリングに自信を持っているのです。

さらに、無料カウンセリングには以下の3大特典もついてきます!

  • 「最短1ヶ月で開発ができる学習方法」電子書籍(非売品)
  • 効率的なオリジナル学習カリキュラム
  • 未経験の転職(フリーランス)を可能にするキャリアサポート

上記の特典だけでも他のスクールにはないポイントだと自信を持っている無料カウンセリング特典です。

ただプログラミング学習をする毎日から、ワクワクしながらプログラミング学習できる毎日に変える体験を一度でいいのでしてみませんか?

無料カウンセリング予約はこちら

まとめ

いかがでしたでしょうか?

この記事では、has_many、belongs_toの使い方を解説しました。

このように関連付けをすることで、関連付けられたidを自動で登録できたり、データをまとめて削除できたりとより簡潔にデータを操作できます。

この記事で紹介した他にも、関連付けをするためのメソッドがありますので、そちらも勉強してみてください。

もしhas_many、belongs_toの使い方について忘れてしまったらこの記事を確認してくださいね!

この記事を書いた人

Unityを使ったiOSアプリのリリース、フリマサイト運営の経験があります。

経験した言語はC、C#、Javascript、R、Python、Ruby、PHPなど

言語が好きで、英語や中国、ドイツ語を勉強しました。
将来的には海外で生活したいです。

現在はRuby on Rails5やCocos2dxの勉強を主にしています。

ライターとしては
できるだけ初心者にわかりやすい文章になるように心がけています。

趣味は語学、読書です。

目次