51
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Rails]コメント削除機能の実装でハマってしまったので一応解決策を。

Last updated at Posted at 2019-09-02

#はじめに
Railsの理解を深めるために、Twitterの機能を自装していく中で、あるTweetに対するコメントの削除機能をつけたところエラーが出続けるという沼にハマってしまいました。
エラー内容はActiveRecord::RecordNotFound (Couldn't find Comment without an ID)です。
スクリーンショット 2019-08-30 17.47.32.png

ParamsでID取得できてませんよーということでした。
一応解決はしたのでメモ程度に書いておきます。

※ 結論としては、ルート設定ミスでした。
#リレーション

スクリーンショット 2019-08-30 18.11.54.png

CommentはUserとTweetの中間テーブルとして作成していきます。
今回のコメントの削除は、どのTweetのCommentか?というところが必要になります。

#エラー時実装内容
まずはエラーが起きてしまった時点での実装を書きます。
##ルート設定

routes.rb

# ---中略--- #

  resources :tweets do
    resources :comments, only: [:create, :destroy]
  end

ネストにしているのはどのツイートに対するコメントかを簡単に設定するためです。
/tweets/:tweet_id/comments/:id(.:format)
スクリーンショット 2019-09-02 10.23.15.png

結論としてはこれが引っかかってたみたいです。。
##コントローラー設定
削除機能に関するコントローラー設定は以下のように実装しました。
Commentテーブルから受け取ったCommentのidに該当するコメントを削除するというものです。

comments_controller.rb
class CommentsController < ApplicationController

# ---中略--- #

  def destroy
    Comment.find(params[:id]).destroy
    redirect_to tweets_path
  end
end

今回は、削除後の先をTweets_pathとしTweet一覧画面に飛ばしてます。
##ビュー設定

前提として、@commentsに投稿されたコメントを引き渡した状態であるとして、、

/tweets/show.html.erb

# ---中略--- #

<% @comments.each do |comment| %>
  <p><span>コメント内容: </span><%= comment.body %></p>
  <p><%=link_to "削除", tweet_comments_path(comment.id), method: :delete %></p>
<% end %>

スクリーンショット 2019-09-02 10.23.15.png

ルーティングを確認すると、Tweet_comments DELEATは
/tweets/:tweet_id/comments/:id(.:format)であるので
パスの書き方はtweet_comments_path
()の中身は取得したいcomment.idを入れました。

##エラー結果
スクリーンショット 2019-08-30 17.45.39.png
これらの実装のもと、「削除」をポチッと押したところ先程のActiveRecord::RecordNotFound (Couldn't find Comment without an ID)というエラーになってしまうというものでした。

#実験
エラー文からきっとtweet_comments_path(comment.id)この書き方が正しくないのだろうと思ったので、試しにtweet_comments_path(comment.tweet.id)としてみることに。

/tweets/show.html.erb

# ---中略--- #

<% @comments.each do |comment| %>
  <p><span>コメント内容: </span><%= comment.body %></p>
  <p><%=link_to "削除", tweet_comments_path(comment.tweet.id), method: :delete %></p>
<% end %>

コントローラーではTweet.idを引き受けます。

comments_controller.rb
class CommentsController < ApplicationController

# ---中略--- #

  def destroy
    comment = Comment.find_by(tweet_id: params[:tweet_id])
    comment.destroy
    redirect_to tweets_path
  end
end

Comment.idの引き渡しはしないので、ネストしている部分はresourcesではなくresourceにします。
resourcesのままだとルーティングエラーになります。

routes.rb

# ---中略--- #

  resources :tweets do
    resource :comments, only: [:create, :destroy]
  end

実際の動かしてみると次のようになりました。
こちらの下の「コメント2つめ」の方を削除します。
スクリーンショット 2019-08-30 18.58.32.png
すると結果は、
スクリーンショット 2019-08-30 19.03.33.png
上の「コメントします」の古い方のコメントが削除されてしまいました。

実験的にTweet.idしか引き渡していないのでそうなりますよね。
どのコメントを削除するか指定していないので。。

けど、ParamでのTweet.idの引き渡しはできたようです。
ルーティングをネストした場合はcomment.tweet.idは引き渡せるけど、comment.idは引き渡せないのか、、よくわからん

※そもそも引き渡し部分をcomment.tweet.idにしなくてはいけなかったのでは?
と思い、ルーティングとコントローラーの設定を戻してみましたが、その場合はcomment.idを渡したいのに、Tweet.id引き渡してしまっているので当然エラーとなりました。

#解決策

ルーティング設定でcommentのdestroy部分をネストさせるのを諦めました。

routes.rb

# ---中略--- #

  resources :tweets do
    resource :comments, only: [:create]
  end
  resources :comments, only: [:destroy]

comments#destroyのルーティングを確認すると以下のようになっています。
スクリーンショット 2019-09-02 12.53.27.png
コントローラーとビューの設定も。

comments_controller.rb
class CommentsController < ApplicationController

# ---中略--- #

  def destroy
    Comment.find(params[:id]).destroy
    redirect_to tweets_path
  end
end
/tweets/show.html.erb

# ---中略--- #

<% @comments.each do |comment| %>
  <p><span>コメント内容: </span><%= comment.body %></p>
  <p><%=link_to "削除", comment_path(comment.id), method: :delete %></p>
<% end %>

このようにしたら無事、コメントの削除をすることができました。
#まとめ
ルーティングをネストしてしまったら取れなくなっていしまう値があるとは考えにくいが、上手くいかなかった。
何かアドバイスがあれば教えていただきたいです。

#変更点
9/2
こちらの問題にアドバイスを頂きまして、解決いたしましたので記述します。

問題点は次の2つでした。

  • tweet_comments_pathではなくtweet_comment_path
  • ルーティングをネストしている場合は引数2つ必要

DELETEならs不要みたいですね。

/tweets/show.html.erb
# ---中略--- #

<% @comments.each do |comment| %>
  <p><span>コメント内容: </span><%= comment.body %></p>
  <p><%=link_to "削除", tweet_comment_path(comment.tweet.id, comment.id), method: :delete %></p>
<% end %>>
comments_controller.rb
class CommentsController < ApplicationController

# ---中略--- #

  def destroy
    Comment.find_by(id: params[:id],tweet_id: params[:tweet_id]).destroy
    redirect_to tweets_path
  end
end
routes.rb

# ---中略--- #

  resources :tweets do
    resources :comments, only: [:create, :destroy]
  end

これでうまくいきました。

51
45
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
45

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?