Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Rails アソシエーションでモデル間に1対多の関係性を持たせる

はじめに

Rails初学者の私がアソシエーションによってテーブルを関連付けた後、
アクションメソッドへパラメーターを受け渡せずに非常に苦労したので記事にしてみます。
ゴールはPlace(カフェ)にメニュー(menus)の情報を持たせて画面上に出力することです。

※初投稿のため理解しがたい部分、間違っている部分等あるかもしれません。
なるべく正しい情報を記述したいと努力していますがご了承いただければと思います。
コメントでご意見等いただけましたら嬉しいです!

ひとまずPlaceとMenuのカラムを確認

irb(main):005:0> Place.column_names
=> ["id", "name", "message", "created_at", "updated_at", "user_id"]
irb(main):006:0> Menu.column_names
=> ["id", "title", "price", "message", "created_at", "updated_at", "place_id"]

上記の通りとなります。

ログインしているUserがお気に入りのカフェを投稿→そのカフェに対してメニューの情報も追加することができる。
という機能です。

アソシエーションによるテーブルの結合

app/models/place.rb
class Place < ApplicationRecord
  belongs_to :user
  has_many :menus
end

Railsでは命名規則が重要なのでこの単数形と複数形の記述を間違えないように注意。
(1人の)userに対して(1つの)place、(1つの)placeに対して(複数の)menusを持ちます。

belongs_to (〜に属する) has_many (たくさん持っている)・・・わかりやすいですね!

app/models/menu.rb
class Menu < ApplicationRecord
  belongs_to :place
end

menu.rbの方はこんな感じ。

とりあえずこれでテーブル間の紐付けは完了しました。

places_controller.rb
    [1] pry(#<PlacesController>)> @place
=> #<Place:0x00007f9d1e88cc10
 id: 3,
 name: "hoge Coffee",
 message: "自宅から最寄りのカフェで落ち着いた雰囲気がとても素敵です。",
 created_at: Thu, 18 Jun 2020 00:05:20 UTC +00:00,
 updated_at: Thu, 18 Jun 2020 13:14:42 UTC +00:00,
 user_id: 1>
[2] pry(#<PlacesController>)> params
=> <ActionController::Parameters {"controller"=>"places", "action"=>"show", "id"=>"3"} permitted: false>

views/places/show.html.erbからメニューの登録を行いたいのでパラメーターを確認します。
こちらが今回メニューを登録したいカフェの情報になります。

places/show.html.erb
  <%= link_to 'メニューを登録', new_menu_path(id: @place.id) %>

メニューの登録をするためにmenus_controllerのnewアクションへ。

この時に_pathヘルパーへ引数を渡しておかないとmenus_controllerのnewアクションでplaceを検索できません。

menus_controller.rb
class MenusController < ApplicationController

  def new
    @menu = Menu.new
    @place = Place.find_by(id: params[:id])
  end
()

入力フォーム↓

menus/new.html.erb
<%= form_with model:@menu, local: true do |f| %>
<h2>メニューを登録</h2>
<p>料理の名前 : <%= f.text_field :title %></p>
<p>Price : <%= f.text_field :price %></p>
<%= f.hidden_field :place_id, :value => @place.id %>
<%= f.submit '登録する' %>
<% end %>

place_idは入力するようなものではないのでhidden_fieldで渡された値を受け取りしてあげます。

createアクションでのplace_idを受け取り方ですがメニューを登録する際の入力フォームで受け取ります。

menus_controller.rb
def create
    # 入力フォームで受けとったパラメーターをここで受け取る
    @menu = Menu.create(
      title: menus_params[:title],
      price: menus_params[:price],
      place_id: menus_params[:place_id]
    )
    # ここでちゃんと受け取れているか確認入れてみます
    binding.pry
end

(中略)

private

  def menus_params
    params.require(:menu).permit(:title, :price, :place_id)
  end

()

確認のためフォームに入力してみます。
結果・・・

[1] pry(#<MenusController>)> menus_params
=> <ActionController::Parameters {"title"=>"アイスコーヒー", "price"=>"280", "place_id"=>"3"} permitted: true>

ちゃんとplace_idを受け取ることができました。

これでちゃんと1対多の関係性(placeに対してmenus)を持ったパラメーターの受け取りができます。

    <% @place.menus.each do |menu| %>
        <p><%= menu.title %> <%= menu.price %></p> 
    <% end %>

登録したメニューはこんな感じで出力できます。

[1] pry(#<PlacesController>)> @place.menus
  ()
=> [#<Menu:0x00007ffda376a4c8
  id: 37,
  title: "チョコレートケーキ",
  price: 250,
  created_at: Thu, 18 Jun 2020 01:36:00 UTC +00:00,
  updated_at: Thu, 18 Jun 2020 01:36:00 UTC +00:00,
  place_id: 3>,
 #<Menu:0x00007ffda3773eb0
  id: 44,
  title: "アイスコーヒー",
  price: 280,
  created_at: Fri, 19 Jun 2020 00:24:50 UTC +00:00,
  updated_at: Fri, 19 Jun 2020 00:24:50 UTC +00:00,
  place_id: 3>]

アソシエーションで紐付けされているので@place.menusで登録したメニューを確認できます。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away