はじめに
私が作成中のオリジナルアプリで中間テーブルの特定の一つのレコードを削除したいときに、起こったエラーについて解説したいと思います。
各々の変数の名前の付け方が下手くそなのは目を瞑っていただけると幸いです。笑
簡単に、私の作成しているのはこのような中間テーブルです
一方的なフォロー機能のようなもので、studentがtextbookに対して「学習を始める」というボタンを押すと、students_textbookという中間テーブルにstudent_idとtextbook_idが一つのレコードに保存されるといった機能です。(イベントに参加するようなイメージの方がわかりやすいかも)
そして、「学習をやめる」ボタンを押すと、その特定の中間テーブルのレコードが削除される仕組みです。
ここでエラーが起きました。
ArgumentError 発生
どうやら、destroyアクションの記述に問題があるようです。
def destroy
@found_recode = StudentsTextbook.where(student_id: current_student.id).where(textbook_id: @textbook.id)
@found_recode.destroy
redirect_to students_textbook_path(@textbook.id)
end
ちなみにbefore_actionで@textbookは下記のように定義されています。
private
def set_textbook
@textbook = Textbook.find(params[:textbook_id])
end
@found_recodeに代入した中身は、students_textbookテーブルの中の特定のレコードを消すために、whereメソッドで現在ログインしているstudent_idが含まれているレコードを全部取り出し、その中からさらに、whereメソッドで学習をやめるtextbookのidが含まれているレコードを取り出すと、特定の消したい一つのレコードが絞り出せると思ったのです。
結果的にこの考え方は間違っていなかったのですが、whereメソッドがどのような形で値を返していたかに問題がありました。あとdestroyの使い方です。
binding_pryで中身を確認
@found_recodeの中身をbinding.pryで調べてみます。
適当なtextbookに「学習をやめる」ボタンを押して@found_recodeを見ると
[#<StudentsTextbook:0x00007fbb7f1d2e90
id: 29,
student_id: 1,
textbook_id: 16,
created_at: Mon, 31 May 2021 17:46:02 JST +09:00,
updated_at: Mon, 31 May 2021 17:46:02 JST +09:00>]
このように表示されました。
たくさんあるレコードの中から特定の目的の一つのレコードが取り出せているかに見えます。しかし、これは配列で返ってきているのです。そして、その次の処理でdestroyメソッドを使用していますが、destroyメソッドは配列には使えないのです。これがエラーの原因でした。
そこでfind_byを使います。
find_byメソッドで解決する
先ほどのdestroyの記述の最後のwhereメソッドだけをfind_byメソッドに変更するだけで解決できました。
def destroy
@found_recode = StudentsTextbook.where(student_id: current_student.id).find_by(textbook_id: @textbook.id)
@found_recode.destroy
redirect_to students_textbook_path(@textbook.id)
end
最後に
今回学んだことはwhereメソッドは配列で値を返していたことと、destroyメソッドは配列には使えないってことですね。
後者は考えたら確かに、、、って感じですね。配列持ってこられても、どうやって削除するのかdestroyさんも困りますよね。すみません。
いつも思うんですが、エラーが発生して自分なりに答えを出して、それが解決につながったときの脳汁の分泌量は半端ないっすね。やめられないです。