Ruby on Rails(以降、Rails)で、レコードを更新する方法はいくつも用意されていることをご存知でしょうか。
今回は、その中からバリデーションやコールバックを実行せずに、レコードをサクッと更新するupdate_allを紹介します。
update_allは、以下のような目的で使えるメソッドです。
・条件に一致したレコードをすべて更新する
・上位3件のレコードのみを更新する
・ランダムに抽出した2件のレコードのみを更新する
この記事では、scaffoldを使ってWebアプリを作成し、update_allの使いかたを具体的に見ていきます。
それでは、はじめましょう。
update_allとは
update_allは、複数のレコードを一括更新するためのメソッドです。
update_allは、以下のように使います。
モデル.update_all(updates)
引数に指定するupdatesには、SQL文のSET部分を表す文字列やハッシュを指定します。
指定方法 | 例 |
---|---|
文字列 | User.update_all("'check' = 'false'") User.update_all("'check' = 'false', 'birth' = '2000-01-01'") |
ハッシュ | User.update_all(check: false) User.update_all(check: false, birth: '2000-01-01') |
User.update_all(“‘check’ = ‘false'”)やUser.update_all(check: false)は、Userテーブルのすべてのレコードのcheckをfalseに変更します。
どちらの書きかたでも同じ動作になります。
通常、Active Recordオブジェクトのメソッドを利用して値を更新すると、バリデーションやコールバックが実行されます。
しかし、update_allは、ActiveRecordオブジェクトを経由しない更新方法のため、バリデーションやコールバックが実行されませんので、注意が必要です。
動作を確認するためのWebアプリを作成する
update_allの雰囲気をつかんだところで、update_allの動作を確認するために、Rails 5.1をインストールしてWebアプリを作りましょう。
(1)Railsをインストールします。
私は、以下の記事を参考に、VirtualBoxで作成した仮想パソコンにインストールしたLinux Mintに、Railsの開発環境を作成しました。
基本的には記事の手順に従って操作しますが、app/samurai/sample1ディレクトリを作成する代わりに、app/samurai/update_all-demoディレクトリを作成しました。
Railsを起動して、ブラウザで画面が表示されることを確認したら、いったんRailsを終了してから次に進みます。
Linux Mintのインストールについては、以下の記事で詳しく説明しています。
(2)Gemfileの最終行に以下の内容を追記します。
gem 'hirb' gem 'hirb-unicode'
Hirbについては、以下の記事で詳しく説明していますので、あわせてご覧ください。
(3)新しい「端末」を起動して、以下のコマンドを1行ずつ順番に入力します。
cd app/samurai/attributes-demo bundle install bin/rails generate scaffold User name:string birth:date check:boolean bin/rails db:migrate bin/rails console
(4)以下のコードを1行ずつ順番に入力します。
User.create(name:"山田太郎", birth:"1991-09-22") User.create(name:"長瀬来", birth:"1991-10-18") User.create(name:"立川裕美", birth:"1968-06-28") User.create(name:"前田達郎", birth:"1984-10-12") User.create(name:"細川修二", birth:"1971-03-24") User.create(name:"木村拓磨", birth:"1995-05-19") exit
Userテーブルにデータが入力され、Railsコンソールが終了します。
update_allでレコードを一括更新する
すべてのレコードを漏れなく更新する
最も簡単な例です。
すべてのレコードのcheckをtrueに変更します。
(1)「端末」で以下のコマンドを入力します。
bin/rails console
(2)以下のコードを1行ずつ順番に入力します。
Hirb.enable User.update_all(check: true) User.all
実行結果:
User Load (0.3ms) SELECT "users".* FROM "users" +----+----------+------------+-------+-------------------------+-------------------------+ | id | name | birth | check | created_at | updated_at | +----+----------+------------+-------+-------------------------+-------------------------+ | 1 | 山田太郎 | 1991-09-22 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 2 | 長瀬来 | 1991-10-18 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 3 | 立川裕美 | 1968-06-28 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 4 | 前田達郎 | 1984-10-12 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 5 | 細川修二 | 1971-03-24 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 6 | 木村拓磨 | 1995-05-19 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | +----+----------+------------+-------+-------------------------+-------------------------+ 6 rows in set
条件に一致したレコードをすべて更新する
40歳未満(birthが40.year.ago以降)のUserに限り、checkをtrueにしてみます。
(1)「端末」で以下のコマンドを入力します。
bin/rails console
(2)以下のコードを1行ずつ順番に入力します。
Hirb.enable User.update_all(check: false) User.where(['birth > ?', 40.year.ago]).update_all(check: true) User.all
実行結果:
User Load (0.1ms) SELECT "users".* FROM "users" +----+----------+------------+-------+-------------------------+-------------------------+ | id | name | birth | check | created_at | updated_at | +----+----------+------------+-------+-------------------------+-------------------------+ | 1 | 山田太郎 | 1991-09-22 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 2 | 長瀬来 | 1991-10-18 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 3 | 立川裕美 | 1968-06-28 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 4 | 前田達郎 | 1984-10-12 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 5 | 細川修二 | 1971-03-24 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 6 | 木村拓磨 | 1995-05-19 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | +----+----------+------------+-------+-------------------------+-------------------------+ 6 rows in set
上位3件のレコードのみを更新する
今度は、上位3位(年長者上位3名)に限り、checkをtrueにする方法です。
(1)「端末」で以下のコマンドを入力します。
bin/rails console
(2)以下のコードを1行ずつ順番に入力します。
Hirb.enable User.update_all(check: false) User.order(:birth).limit(3).update_all(check: true) User.all
実行結果:
User Load (0.1ms) SELECT "users".* FROM "users" +----+----------+------------+-------+-------------------------+-------------------------+ | id | name | birth | check | created_at | updated_at | +----+----------+------------+-------+-------------------------+-------------------------+ | 1 | 山田太郎 | 1991-09-22 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 2 | 長瀬来 | 1991-10-18 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 3 | 立川裕美 | 1968-06-28 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 4 | 前田達郎 | 1984-10-12 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 5 | 細川修二 | 1971-03-24 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 6 | 木村拓磨 | 1995-05-19 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | +----+----------+------------+-------+-------------------------+-------------------------+ 6 rows in set
orderとlimitを組み合わせて使うと、一見難しそうな更新も簡単に実現できました。
ランダムに抽出した2件のレコードのみを更新する
少し目先を変えて、ランダムに抽出した2件のレコードのみ、checkをtrueにしてみます。
抽選に使うイメージですね。
(1)「端末」で以下のコマンドを入力します。
bin/rails console
(2)以下のコードを1行ずつ順番に入力します。
Hirb.enable User.update_all(check: false) User.where('id >= ?', rand(User.first.id..User.last.id)).limit(2).update_all(check: true) User.all
実行結果:
User Load (0.1ms) SELECT "users".* FROM "users" +----+----------+------------+-------+-------------------------+-------------------------+ | id | name | birth | check | created_at | updated_at | +----+----------+------------+-------+-------------------------+-------------------------+ | 1 | 山田太郎 | 1991-09-22 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 2 | 長瀬来 | 1991-10-18 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 3 | 立川裕美 | 1968-06-28 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 4 | 前田達郎 | 1984-10-12 | false | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 5 | 細川修二 | 1971-03-24 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | | 6 | 木村拓磨 | 1995-05-19 | true | 2018-07-24 02:16:01 UTC | 2018-07-24 02:16:01 UTC | +----+----------+------------+-------+-------------------------+-------------------------+ 6 rows in set
※毎回、実行結果が異なります。
その他の更新メソッド
初めにも書きましたが、update_allではバリデーションやコールバックが実行されません。
バリデーションやコールバックが必要なら、updateやupdate_attributesを使います。
Railsで利用できる、値を更新するメソッドについては、以下の記事で詳しく説明していますので、あわせてご覧ください。
まとめ
今回は、Railsでレコードを更新する方法の一つであるupdate_allの使いかたを紹介しました。
whereやorderと組み合わせてupdate_allを使うと、条件に一致したレコードだけ一括して値を変更できました。
それから、update_attributesなどとは異なり、update_allはバリデーションやコールバックが実行されないため、おかしな値に変更しないように十分注意しましょう。
update_allに限らず、こういったフレームワークでは、似たような機能のメソッドとの違いを理解して使用することが大切です。
一つ一つ丁寧に動作を確認しながら使用していきましょう。
それでは!