質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.62%

(Rails)多対多で関連付けられているモデルを取得できない

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,192

score 214

質問文通りですが多対多で関連付けられているモデルが取得できません。
ER図で書くと以下のようなリレーションになっております。
なお、ユーザーモデルはdevise製のものです。

イメージ説明

一つのユーザーは応募テーブルを介して多数の仕事を持っており、
仕事テーブルは応募テーブルを介して多数のユーザーをもっているという作りになっています。

ActiveRecordのリレーションは以下のように書いています。

#TWorkモデル
class TWork < ApplicationRecord
  has_many :user_accounts, through: :t_applicants, source: :t_user
end

#TUserモデル
class TUserAccount < ApplicationRecord
  has_many :t_works, through: :t_favorite, source: :t_work
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :confirmable

#TApplicantモデル
class TApplicant < ApplicationRecord
  belongs_to :t_work
  belongs_to :t_user
end

以上の条件を踏まえて現在ログインしているユーザーが応募している 仕事の値(例では仕事のタイトル)を取ってきたいのですが、メソッドの書き方が悪いのか、リレーションメソッドの書き方がおかしいのか取得できません。取得しているメソッドは以下のようにしています。

#userモデル
class TUserAccount < ApplicationRecord
  has_many :t_works, through: :t_favorite, source: :t_work
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :confirmable

 def show_work(user_id)
#ここのメソッドの中身をどう書けばいいかわからない
 TUser.includes(:t_works).where(t_user: {id: user_id})
 end

end

#userコントローラー/TUserモデルをインスタンス化し、show_workメソッドを呼び、引数に現在のユーザーのidを渡す
class TUserController < ApplicationController
  def show
  worklist = TUser.new
  @worklist = worklist.show_work(params[:id])
 #もしくは
 @worklist = worklist.show_work(current_t_user.id)
  end
end

<!-- show.html.erb -->
<h1>応募中仕事一覧</h1>
<% @worklist.each do |work| %>
<%= work.title %>
<% end %>

こうすると@worklistはnilclassやら、少しいじるとミススペルじゃないか?等エラーを吐きます。

コメントにも書いておりますが、分からないことはdef show_workメソッドの中身の書き方と、またモデルクラスのhas_many_thoroughメソッドのオプション等の書き方です。
この3つのテーブルを全て結合し、候補者テーブルの外部キーt_user_idが現在ログインしているユーザーとマッチした仕事テーブルの値を取るということをしたいのですがどうすればよいでしょうか?
色々調べて試したのですが3日間くらい詰まっています。

長くなり申し訳ございませんが、よろしくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

0

def show_work(user_id)
#ここのメソッドの中身をどう書けばいいかわからない
 TUser.includes(:t_works).where(t_user: {id: user_id})
 end


このメソッドに関しては、まずTUserがt_worksのような子テーブルを持っている訳ではないのでinvalidになっているのだと思います。

なので、t_users_controller.rbのshowアクションの中で、

@worklist = T_applicant.includes(:t_work).where(t_user_id: @t_user.id)


などとして見るのが良いのかなと思います。本来、includeをメソッドを利用する目的としては、アソシエーションを用いてt_applicants.userなどとするとN+1問題が発生してしまうので、そのための対策としてincludeメソッドで先にt_worksの情報を取得する...といった手順をとります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/25 03:43

    無事解決できました!
    ありがとうございます!

    キャンセル

0

まず、モデルの部分のTWorkに関して、

class TWork < ApplicationRecord
  has_many :user_accounts, through: :t_applicants, source: :t_user
end


この部分ですが、t_worksからみている中間テーブルとの関係を記述していないことが問題だと思われます。参照している中間テーブルがないのが一つ目の問題です。

ですので、以下のように変更するべきだと思います。

class TWork < ApplicationRecord
  has_many :t_applicants
  has_many :user_accounts, through: :t_applicants, source: :t_user
end

また、TUserモデルに関しても中間テーブルを参照しておらず、中間テーブルの名前も誤りがあります。

#TUserモデル
class TUserAccount < ApplicationRecord
  has_many :t_works, through: :t_favorite, source: :t_work
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :confirmable


なので、こちらもhas_many :t_applicantsが必要です。

多対多の関係は、あくまで二つの一対多の関係の関係ですので、そちらに気をつけてコーディングしていくと良いのではないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/25 02:16

    ご回答ありがとうございます!
    リレーションメソッドの書き方を知らずthroughオプションをつければよいと考えていました。
    ちなみになのですが、、、show_workメソッドの中身はどのようにすればいいでしょうか。。。
    やりたいことは 
    >候補者テーブルの外部キーt_user_idが現在ログインしているユーザーとマッチした仕事テーブルの値を取る
    ということなのですが、、、
    言われたとおり再度リレーションメソッドを書き加え、しっかりとした名前の指定を行い
    def show_work(user_id)
    TWork.includes(:t_applicants).where(t_performer_account_id: user_id)
    end
    としてみたのですが値が入ってきていないのかstatement invalidが出てきてしまい。。。
    よろしくお願い致します

    キャンセル

0

TUserモデル

class TUserAccount < ApplicationRecord
  has_many :t_applicants
  has_many :t_works, through: :t_applicants, source: :t_work
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :confirmable
end

候補者テーブルの外部キーt_user_idが現在ログインしているユーザーとマッチした仕事テーブルの値を取る

作成したアソシエーションを使いましょう。

class TUserController < ApplicationController
  def show
    # ログインしているユーザーを仮にcurrent_userとする
    @worklist = current_user.t_works
  end
end

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る