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

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

ただいまの
回答率

89.22%

[フォロー機能実装] Viewにおけるform_for使用時の undefined method エラーの解決法について

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 529

SELA

score 13

初学者の者で、現在Qiitaの下記の記事を参考にさせて頂きながらフォロー機能の実装を試みております。
Qiita:railsでフォロー機能をつける。

下記のようなエラーが生じて解決法が見出せていないために、今回ご質問させていただきます。
以下、詳細を記述いたします。

環境

ruby 2.5.3
rails 5.2.3


事象

特定userの詳細ページにて「フォローする」ボタンを表示させようとして当該ページ(http://localhost:3000/users/2)にアクセスしたところ、下記のエラーが発生しました。

undefined method `relationships_path' for #<#<Class:0x00007fa354d636a8>:0x00007fa354d61c18>
  • 対象のViewファイル
    現在のログインユーザーであるcurrent_user(Gem deviseにより定義)について、後述のfollowing_relationshipsのテーブルを生成しようとしています。
<%= form_for current_user.following_relationships.new(following_id: @user.id) do |f| %>
  <div><%= f.hidden_field :following_id %></div>
  <%= f.submit "フォローする", class: "btn btn-sm btn-primary follow-btn" %>
<% end %>
  • Model
    上記内でのメソッドfollowing_relationshipsの定義元はUserモデル(app/models/user.rb)内にあります。
    以下、フォロー機能周りの記述の抜粋
class User < ApplicationRecord

  has_many :following_relationships, foreign_key: "follower_id",
           class_name: "Relationship", dependent: :destroy

  has_many :followings, through: :following_relationships

  has_many :follower_relationships, foreign_key: "following_id",
           class_name: "Relationship", dependent: :destroy

  has_many :followers, through: :follower_relationships

  # ユーザー登録機能はGem deviseを使用
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable,:trackable, :validatable

  def following?(other_user)
    following_relationships.find_by(following_id: other_user.id)
  end

 # 次行のfollow!は後述のCreateアクション内で使用
  def follow!(other_user)
    following_relationships.create!(following_id: other_user.id)
  end

  def unfollow?(other_user)
    following_relationships.find_by(following_id: other_user.id).destroy
  end
end

Userモデルに依存するRelationshipモデルは下記であり、モデル間の関連付けやカラムの整合は取れているように思います。

class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :following, class_name: "User"
  validates :follower_id, presence: true
  validates :following_id, presence: true
end


また、relationshipsテーブルの構成は、db/schema.rbより

  create_table "relationships", force: :cascade do |t|
    t.integer "follower_id"
    t.integer "following_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["follower_id", "following_id"], name: "index_relationships_on_follower_id_and_following_id", unique: true
    t.index ["follower_id"], name: "index_relationships_on_follower_id"
    t.index ["following_id"], name: "index_relationships_on_following_id"
  end
  • Controller
    以下は、狙いのcreateアクションについての記述です。
class RelationshipsController < ApplicationController
  def create
    @user = User.find(params[:relationship][:following_id])
    current_user.follow!(@user)
    if following_relationships.save
      flash[:notice] = "フォローしました!"
      redirect_back(fallback_location: root_path)
    else
      flash[:notice] = "フォローに失敗しました。"
      redirect_back(fallback_location: root_path)
    end
  end
end
  • Routing
    routes.rbのRelationshipsについての記述部分です。
Rails.application.routes.draw do

  resources :users do
    member do
      get :following, :followers
    end
    resources :relationships, only: [:create, :destroy]
  end

end


上記によりPATHは以下のとおり生成されています。

           following_user GET    /users/:id/following(.:format)                                                           users#following
           followers_user GET    /users/:id/followers(.:format)                                                           users#followers
       user_relationships POST   /users/:user_id/relationships(.:format)                                                  relationships#create
        user_relationship DELETE /users/:user_id/relationships/:id(.:format)                                              relationships#destroy

以上から、Viewから関係性を遡っていき、following_relationshipsメソッドに紐づく各モデル間の関連付けやカラムの整合、ルーティングの生成は出来ているように思います。
静的コード解析ツールのrubocopでもチェックしましたが、全角文字混入の指摘なども確認されなかったので、なぜパスが通らずにうまくいかないのか頭を悩ませております。

根本的な基礎事項の理解が至らずに生じた些細なエラーなのかもしれませんが、form_forの使用法や、既存の質疑などの情報を参照しても解決法が見つかっていないために、ここでご相談させて頂きます。
お手数をお掛けいたしますが、ご助言やご指摘のほど宜しくお願い申し上げます。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

relationships_pathではなくuser_relationships_pathにフォームを送信するには

<%= form_for [親モデル, 子モデル] do |f| %>


でフォームを作成しましょう。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/06/30 13:37

    早々にご回答頂きありがとうございました。
    form_forの基本的な使用法を理解出来ておりませんでした。

    ご教示頂いた記法でコードを修正して機能の実装に成功することが出来ました。
    重ねてお礼申し上げます。

    キャンセル

0

Mugheartさんにご教示頂いた記法に基づいてViewファイルのコードを修正しました。
一連の下記コードにより、ローカルホスト及びHerokuサーバー上においてフォロー/解除機能が正常に動作することを確認致しました。
また、MVC各部のメソッドや変数の定義については再考して変更を加えており、参考先記事の内容とは異なる部分があるので、以下のコードを参考にされる方はその点にご留意ください。
(ご参考までに、本質問で触れなかったdestroyアクションについても合わせて記載しておきます。)

VIEW

<!– フォローボタン –>
<%= form_for [current_user,  @user.following_relationships.new(following_id: @user.id)] do |f| %>
  <div><%= f.hidden_field :following_id %></div>
  <%= f.submit "フォローする", class: "btn btn-sm btn-primary" %>
<% end %>
<!– フォロー解除ボタン –>
<%= form_for [current_user, current_user.following_relationships.find_by(following_id: @user.id)], html: { method: :delete } do |f| %>
  <%= f.submit "フォローを解除する", class: "btn btn-sm btn-secondary" %>
<% end %>

MODEL

class User < ApplicationRecord

  # 関係部分抜粋(関連付けは変更なし) 
  has_many :following_relationships, foreign_key: "follower_id",
           class_name: "Relationship", dependent: :destroy
  has_many :followings, through: :following_relationships
  has_many :follower_relationships, foreign_key: "following_id",
           class_name: "Relationship", dependent: :destroy
  has_many :followers, through: :follower_relationships

  def following?(other_user)
    following_relationships.find_by(following_id: other_user.id)
  end

  def follow!(other_user)
    following_relationships.create!(following_id: other_user.id)
  end

  def unfollow!(other_user)
    following_relationships.find_by(follower_id: other_user.id)
  end

end
class Relationship < ApplicationRecord
  # 質問投稿時より変更なし
  belongs_to :follower, class_name: "User"
  belongs_to :following, class_name: "User"
  validates :follower_id, presence: true
  validates :following_id, presence: true
end

CONTROLLER

class RelationshipsController < ApplicationController
  def create
    @user = User.find(params[:relationship][:following_id])
    @relationship = current_user.follow!(@user)
    @relationship.save
    if @relationship.save
      flash[:notice] = @user.username += "さんをフォローしました!"
      redirect_back(fallback_location: root_path)
    else
      flash[:notice] = @user.username += "さんのフォローに失敗しました。"
      redirect_back(fallback_location: root_path)
    end
  end

  def destroy
    @user = Relationship.find(params[:id]).follower
    followed = Relationship.find(params[:id]).following
    @relationship = current_user.unfollow!(@user)
    @relationship.destroy
    if @relationship.destroy
      flash[:notice] = followed.username += "さんのフォローを解除しました。"
      redirect_back(fallback_location: root_path)
    else
      flash[:notice] = followed.username += "さんのフォローの解除に失敗しました。"
      redirect_back(fallback_location: root_path)
    end
  end
end

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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