更新日:
【Rails】 destroyメソッドの使い方とは?
destroyメソッドとは、すでにテーブルに存在するレコードを削除するメソッドです。
1
インスタンス.destroy
1
2
3
article = Article.find(1)
article.destroy
# articlesテーブルのidが1のレコードを削除
destroyメソッドの使い方
この章では、destroyメソッドの使い方について解説します。
destroyメソッドの引数
destroyメソッドの引数に削除したいレコードのidを指定しても削除できます。
1
2
3
4
Article.destroy(削除したいレコードのid)
# articlesテーブルのidが3のレコードを削除
Article.destroy(3)
ただこの記述法はあまり使いません。
この際、引数に指定したidのレコードが存在しない場合は例外が発生します。
1
2
3
4
[1] pry(main)> Artcile.destroy(3)
(34.8ms) SET NAMES utf8, @@SESSION.sql_mode = CONCAT(CONCAT(@@sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), @@SESSION.sql_auto_is_null = 0, @@SESSION.wait_timeout = 2147483
Article Load (1.6ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`id` = 3 LIMIT 1
ActiveRecord::RecordNotFound: Couldn't find Article with 'id'=3
実際にdestroyメソッドを使ってみよう
それでは実際のアプリでdestroyメソッドを使ってみます。
削除機能を実装するまでの流れをみていきましょう。
- ルーティングを設定
- パスの指定
- destroyアクションを定義
- 削除するレコードのidの指定
- レコードの削除
1. ルーティングを設定
destroyメソッドは、destroyアクション内で使うのでdestroyアクションが動くルーティングを設定します。
1
delete 'articles/:id' => 'articles#destroy'
2.パスの指定
ビューファイル内のdestroyアクションを動かすパスを"articles/#{item.id}"と指定します。
ルーティングを定義したので、リンクもそのように記述します。
:id
の部分にはarticleのidを入れたいので、article.id
とします。
1
<%= link_to "削除", "articles/#{article.id}", method: :delete %>
3.destroyアクションを定義
コントローラーのdestroyアクション内でdestroyメソッドを使用します。
1
2
3
4
def destroy
article = article.find("削除するレコードのid")
article.destroy
end
findメソッドの引数には削除するレコードのidが入ります。
今回はルーティングで「'articles/:id'」と指定しました。
こう記述するとパスの:id
に入っている値をコントローラーではparams[:id]
とすることで取得することができます。
4.削除するレコードのidの指定
コントローラーのdestroyアクションの引数に削除するレコードのidを指定します。
「'articles/:id'」の:id
の部分はビューファイルでは「"articles/#{article.id}"」と記述しています。params[:id]
としてあげれば削除したいレコードのidを取得できます。
1
2
3
4
def destroy
article = Article.find(params[:id])
article.destroy
end
5.レコードの削除
destroyメソッドが実行され、レコードが削除されます。
これでidを指定できたので、findメソッドで削除したいレコードを取得します。それを変数articleに代入し、articleにdestroyメソッドを使って指定したidのレコードを削除します。
destroyメソッドはコントローラーのどこに書くか
destroyメソッドは、コントローラーの7つのアクションのうち、destroyの中に記述します。
アクション名 | 機能 |
---|---|
index | リソースの一覧を表示させる |
show | リソースの詳細を表示させる |
new | 投稿フォームを表示させる |
create | リソースを追加させる |
edit | 更新フォームを表示させる |
update | リソースを更新させる |
destroy | リソースを削除する |
Railsではこの7つのアクションに従ってメソッドを記述することが、可読性を高める上で重要です。使いわけに自信がない場合は、「resourcesメソッドの使い方」をご参照ください。
dependentオプション
2つのテーブルで下記のようにアソシエーションを組んでいるとします。
1
2
3
4
5
# article.rb
belongs_to :user
# user.rb
has_many :articles
このとき、usersテーブルのレコードが削除されたら、それに関連しているarticlesテーブルのレコードも削除しないとエラーが発生してしまいます。
例えばidが5のユーザーが投稿した記事が10個あったとします。
このとき、idが5のユーザーを削除したらidが5のユーザーが投稿した記事が存在しているとおかしなことになりますよね。
そんな時はモデルでdependentオプションを定義します。
1
has_many :articles, dependent: :destroy
上のようにdestroyを定義するとユーザーが削除された際、それに関連するarticlesテーブルのレコードも同時にdestroyメソッドが実行され削除されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[1] pry(main)> user = User.find(5)
[2] pry(main)> user.destroy
(0.2ms) BEGIN
Article Load (0.4ms) SELECT `articles`.* FROM `articles` WHERE `articles`.`user_id` = 5
SQL (1.1ms) DELETE FROM `articles` WHERE `articles`.`id` = 74
SQL (0.3ms) DELETE FROM `articles` WHERE `articles`.`id` = 90
SQL (0.2ms) DELETE FROM `articles` WHERE `articles`.`id` = 92
SQL (0.2ms) DELETE FROM `articles` WHERE `articles`.`id` = 93
SQL (0.2ms) DELETE FROM `articles` WHERE `articles`.`id` = 97
SQL (0.2ms) DELETE FROM `articles` WHERE `articles`.`id` = 98
SQL (0.4ms) DELETE FROM `articles` WHERE `articles`.`id` = 99
SQL (0.3ms) DELETE FROM `articles` WHERE `articles`.`id` = 100
SQL (0.5ms) DELETE FROM `articles` WHERE `articles`.`id` = 101
SQL (0.2ms) DELETE FROM `articles` WHERE `articles`.`id` = 103
SQL (0.2ms) DELETE FROM `users` WHERE `users`.`id` = 5
(2.6ms) COMMIT
=> #<User:0x007feecad4e368
id: 5
name: "ピカわか",
created_at: XXX, XX XXX 20XX XX:XX:XX JST +09:00,
updated_at: XXX, XX XXX 20XX XX:XX:XX JST +09:00>
このようにusersテーブルのidが5のレコードだけでなく、articlesテーブルのuser_idカラムの値が5のレコードを全て削除してくれます。
deleteメソッドとの違い
destroyメソッドのようにレコードを削除するメソッドにdeleteメソッドがあります。
destroyメソッドはデータを削除するときにActiveRecord、つまりモデルを介すのに対し、deleteメソッドはActiveRecordを介さずにSQLを直接実行してデータを削除します。
モデルを介さないので、deleteメソッドは先ほど紹介した「dependentオプション」は実行されません。
1
2
3
4
5
6
7
8
[1] pry(main)> user = User.find(5)
[2] pry(main)> user.delete
User Destroy (8.8ms) DELETE FROM `users` WHERE `users`.`id` = 5
=> #<User:0x007feecad4e368
id: 5
name: "ピカわか",
created_at: XXX, XX XXX 20XX XX:XX:XX JST +09:00,
updated_at: XXX, XX XXX 20XX XX:XX:XX JST +09:00>
destroy!メソッドとの違い
destroyメソッドは削除が成功すると上の例のように削除したインスタンスを、削除が失敗するとfalseを返します。
destoy!メソッドの方は、削除が行われなかった時にActiveRecord::RecordNotDestroyed例外を発生させます。
1
2
3
4
5
6
[1] pry(main)> user = User.find(5)
[2] pry(main)> user.destroy
(12.4ms) BEGIN
Article Exists (41.7ms) SELECT 1 AS one FROM `articles` WHERE `articles`.`user_id` = 5 LIMIT 1
(6.5ms) ROLLBACK
=> false
この場合、条件分岐をしておかないと何らかの原因で削除がされなくても成功した時と同じ挙動をします。
このように削除ボタンを押しても削除されていないのに、削除された時と同じようにトップページへリダイレクトされてしまいました。
これでは削除されなかった時にすぐに気づくことができません。
1
2
3
4
5
6
[1] pry(main)> user = User.find(5)
[2] pry(main)> user.destroy!
(1.0ms) BEGIN
Article Exists (1.3ms) SELECT 1 AS one FROM `articles` WHERE `articles`.`article_id` = 5 LIMIT 1
(1.1ms) ROLLBACK
ActiveRecord::RecordNotDestroyed: Failed to destroy the record
それに対し、destroy!メソッドを使っておくと削除ができなかった場合は下記のようなエラー文が出るので、削除できなかったことがすぐに確認できます。
実際はエラーが出てしまうと困るので、destroyメソッドを使い、if文で条件分岐をしておく方が良いでしょう。
1
2
3
4
5
6
7
8
def destroy
article = Article.find(params[:id])
if article.destroy
redirect_to root_path, notice: "削除が完了しました"
else
redirect_to root_path, alert: "削除が失敗しました"
end
end
ユーザーが投稿したレコードのみ削除できるようにしよう
ユーザーが記事を投稿できるアプリがあったとします。そのアプリに削除機能をつけるのですが、もしユーザーが全ての投稿を削除できてしまったらどうなるでしょう?
悪意のあるユーザーが全てのレコードを削除してしまうかもしれません。それでは困りますよね。
その為ユーザーが投稿したレコードを削除する際、基本的に自分が投稿したレコードしか削除できないよう設定をしておく必要があります。
それには削除ボタン自体を表示させなくすれば良いので、ビューファイルを下記のように記述します。
1
2
3
<% if user_signed_in? && current_user.id == article.user_id %>
<%= link_to "削除", "articles/#{article.id}", method: :delete %>
<% end %>
上のコードの意味はユーザーがログインしていて、かつログインしているユーザーのidが投稿したレコードのuser_idと同じであれば削除ボタンが表示されます。
さらにコントローラー側でも下記のように記述しておきましょう。
1
2
3
4
5
6
7
8
9
10
def destroy
article = Article.find(params[:id])
if article.user_id == current_user.id
if article.destroy
redirect_to root_path, notice: "削除が完了しました"
else
redirect_to root_path, alert: "削除が失敗しました"
end
end
end
このようにRails側でも念の為に削除したいレコードのidが今ログインしているユーザーのidと等しいという条件をつけておきましょう。Railsについてもっと学びたい!という方は、複数人での開発体制やテストコードの書き方など実際の開発現場での役に立つこちらの参考書が良いでしょう。
この記事のまとめ
- destroyメソッドはレコードを削除するときに使用するメソッド
- dependentオプションでdestroyを定義すると関連したレコードも削除することができる
- deleteメソッドと違い、モデルを介して削除を行う