スライドショースライドショー

【Rails入門】Active Record(O/Rマッパー)でattributesを更新する方法

今日は、Ruby on Rails(以降、Rails)のActive Record(O/Rマッパー)で、attributesを更新する方法をいくつか紹介します。

・attributeやassign_attributesで更新したデータが、元に戻ってしまうんだけど…
・has_manyの関係を持つ入れ子のデータを一括して更新する方法はないものか?
・データベースに保存するまでもないようなattributesを簡単に保持しておくことはできない?

といった疑問に答える記事になっています。

例によって、動作を理解するためのWebアプリを作ることから始めていますので、実際に操作をしながら、動作を理解していってください!

それでは、始めましょう。

Active RecordおよびO/Rマッパーとは

Active Recordは、データベースに保存されるデータを取り扱うためのコンポーネントです。

Railsでは、Active Recordを通して、データベースからデータを取り出したり、データベースにデータを保存したりします。

O/Rマッパーは、データベースとプログラムを橋渡しする役目を担うモノです。

データベースに保存されているデータをプログラムで利用するには、SQL文を利用してデータベースからデータを取り出し、そのデータをプログラムで扱う変数に格納します。

この処理をスムーズに行うモノがO/Rマッパーです。

RailsではActive RecordにO/Rマッパーが含まれているため、Active Recordを使えば、データベースからデータを取り出したり、データベースにデータを保存したりといった処理がスムーズに行える、と考えればよいでしょう。

attributesとは

この記事で取り扱うattributesは、モデルに含まれるすべての属性を指します。

以下のコマンドでUserモデルを作成したときに、

bin/rails generate scaffold User name:string

「@user.name」でアクセスできる「name」のことを、attributesと呼んでいます。

動作を理解するためのWebアプリを作成する

attributesの取り扱いを理解するために、RailsをインストールしてWebアプリを作りましょう。

(1)Railsをインストールします。

私は、以下の記事を参考に、VirtualBoxで作成した仮想パソコンにインストールしたLinux Mintに、Railsの開発環境を作成しました。

基本的には記事の手順に従って操作しますが、app/samurai/sample1ディレクトリを作成する代わりに、app/samurai/attributes-demoディレクトリを作成しました。

Railsを起動して、ブラウザで画面が表示されることを確認したら、いったんRailsを終了してから次に進みます。

初心者でもかんたん!Ruby on Rails の開発環境の構築手順(Mac/Windows 両対応)
更新日 : 2019年8月9日

Linux Mintのインストールについては、以下の記事で詳しく説明しています。

Linux Mint Cinnamonエディションを使ってみよう
更新日 : 2019年5月22日

(2)Gemfileの最終行に以下の内容を追記します。

gem 'hirb'
gem 'hirb-unicode'

Hirbについては、以下の記事で詳しく説明していますので、あわせてご覧ください。

【Rails入門】初心者が知っておくべき3つのgem
更新日 : 2019年8月9日

(3)新しい「端末」を起動して、以下のコマンドを1行ずつ順番に入力します。

cd app/samurai/attributes-demo
bundle install
bin/rails generate scaffold User name:string
bin/rails generate scaffold Post user_id:integer title:string month:integer
bin/rails db:migrate
bin/rails console

(4)以下のコードを1行ずつ順番に入力します。

User.create(name:"山田太郎")
User.create(name:"長瀬来")
User.create(name:"立川裕美")
User.create(name:"前田達郎")
User.create(name:"細川修二")
User.create(name:"木村拓磨")
Post.create(user_id:5, title:"楽しい休日の過ごし方", month:3)
Post.create(user_id:1, title:"先日の旅行での話", month:2)
Post.create(user_id:3, title:"昨日の出来事", month:12)
Post.create(user_id:3, title:"山登りに行きました", month:8)
Post.create(user_id:4, title:"友人が結婚しました", month:4)
Post.create(user_id:2, title:"最近少し気になったこと", month:1)
Post.create(user_id:4, title:"ランニングのコツ", month:9)
Post.create(user_id:3, title:"Ruby on Railsの日", month:9)
exit

UserテーブルとPostテーブルにデータが入力され、Railsコンソールが終了します。

次にUserテーブルとPostテーブルを関連付けます。

(5)app/models/user.rbを以下のように編集します。

変更前:

class User < ApplicationRecord
end

変更後:

class User < ApplicationRecord
  has_many :posts
  accepts_nested_attributes_for :posts
end

(6)app/models/post.rbを以下のように編集します。

変更前:

class Post < ApplicationRecord
end

変更後:

class Post < ApplicationRecord
  belongs_to :user
end

これで、準備ができました。

データのattributesを更新する

データベースのデータのattributesを更新する方法を説明します。

まずは、保存するタイミングが異なる2つの方法を紹介しましょう。

データのattributesを一括で更新してあとで保存する場合

データのattributesを一括で更新して、あとでデータベースに保存する方法を説明します。

具体的には、attributesまたはassign_attributesを使ってattributesを一括で更新し、saveを使ってデータベースに保存します。

(1)「端末」で以下のコマンドを入力します。

bin/rails console

(2)以下のコードを1行ずつ順番に入力します。

Hirb.enable
@post = Post.find(1)
@post.attributes = { title: '日曜日と祝日の朝ごはん', month: 5 }
@post

1番目のPostデータのattributes(titleとmonth)が一括で更新されます。

実行結果:

+----+---------+------------------------+-------+-------------------------+-------------------------+
| id | user_id | title                  | month | created_at              | updated_at              |
+----+---------+------------------------+-------+-------------------------+-------------------------+
| 1  | 5       | 日曜日と祝日の朝ごはん | 5     | 2018-07-17 14:20:46 UTC | 2018-07-17 14:20:46 UTC |
+----+---------+------------------------+-------+-------------------------+-------------------------+
1 row in set

なお、3行目のコードは、以下のコードでも同じ動作になります。

@post.assign_attributes({ title: '日曜日と祝日の朝ごはん', month: 5 })

ただし、この時点ではデータベースには保存されていません。

いったんRailsコンソールを再起動して、1番目のPostデータを確認してみましょう。

(3)以下のコードを入力します。

exit

Railsコンソールが終了します。

(4)以下のコマンドを入力します。

bin/rails console

(5)以下のコードを入力します。

Hirb.enable
@post = Post.find(1)

実行結果:

  Post Load (0.3ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
+----+---------+----------------------+-------+-------------------------+-------------------------+
| id | user_id | title                | month | created_at              | updated_at              |
+----+---------+----------------------+-------+-------------------------+-------------------------+
| 1  | 5       | 楽しい休日の過ごし方 | 3     | 2018-07-17 14:20:46 UTC | 2018-07-17 14:20:46 UTC |
+----+---------+----------------------+-------+-------------------------+-------------------------+
1 row in set

残念ながら元の内容に戻っていますので、先ほどのattributesで変更した内容が保存されていないことがわかります。

(6)以下のコードを1行ずつ順番に入力します。

@post.attributes = { title: '日曜日と祝日の朝ごはん', month: 5 }
@post.save()
exit

@post.save()を実行したため、今度はデータベースに保存されました。

同じようにいったんRailsコンソールを再起動して確認してみましょう。

(7)以下のコマンドを入力します。

bin/rails console

(8)以下のコードを1行ずつ順番に入力します。

Hirb.enable
@post = Post.find(1)

実行結果:

  Post Load (0.2ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
+----+---------+------------------------+-------+-------------------------+-------------------------+
| id | user_id | title                  | month | created_at              | updated_at              |
+----+---------+------------------------+-------+-------------------------+-------------------------+
| 1  | 5       | 日曜日と祝日の朝ごはん | 5     | 2018-07-17 14:20:46 UTC | 2018-07-17 14:28:07 UTC |
+----+---------+------------------------+-------+-------------------------+-------------------------+
1 row in set

今度はしっかりと保存されていますね!

データのattributeを一括で更新してすぐに保存する場合

データのattributeを一括で更新して、すぐにデータベースに保存する方法を説明します。

具体的には、update_attributesを使ってデータを一括で更新します。

(1)「端末」で以下のコマンドを入力します。

bin/rails console

(2)以下のコードを1行ずつ順番に入力します。

@post = Post.find(1)
@post.update_attributes({ user_id: 2, title: '金曜日のディナー', month: 2 })
exit

例によって、Railsコンソールを再起動して確認してみましょう。

(3)以下のコマンドを入力します。

bin/rails console

(4)以下のコードを入力します。

Hirb.enable
@post = Post.find(1)

実行結果:

  Post Load (0.1ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
+----+---------+------------------+-------+-------------------------+-------------------------+
| id | user_id | title            | month | created_at              | updated_at              |
+----+---------+------------------+-------+-------------------------+-------------------------+
| 1  | 2       | 金曜日のディナー | 2     | 2018-07-17 14:20:46 UTC | 2018-07-17 14:33:19 UTC |
+----+---------+------------------+-------+-------------------------+-------------------------+
1 row in set

しっかりと保存されています。

(5)以下のコードを入力します。

exit

ストロングパラメータを利用する

ストロングパラメータ(Strong Parameter)は、送られてきたパラメータを安全に、データのattributesに反映するための仕組みです。

ストロングパラメータを使用せずに発生するぜい弱性を、マスアサインメント(Mass Assignment)ぜい弱性と呼びます。

ストロングパラメータやマスアサインメントぜい弱性については、以下の記事でも触れていますので、あわせてご覧ください。

【Rails入門】params使い方まとめ
更新日 : 2019年7月31日

入れ子のデータのattributesをまとめて更新する

ここからは、入れ子のデータのattributesを、以下のようにまとめて更新する方法を紹介します。

@user.attributes = {
    name:"侍エンジニアさん",
    posts_attributes:[
        {id:3, title: "きのうのできごと"},
        {id:4, title:"あしたのよてい", month: 5},
        {id:8, title:"Railsのひ", month: 10}
    ]
}

(1)以下のコマンドを入力します。

bin/rails console

(2)以下のコードを1行ずつ順番に入力します。

Hirb.enable
@user = User.find(3)
@user.posts

実行結果:

  Post Load (0.3ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 3]]
+----+---------+--------------------+-------+-------------------------+-------------------------+
| id | user_id | title              | month | created_at              | updated_at              |
+----+---------+--------------------+-------+-------------------------+-------------------------+
| 3  | 3       | 昨日の出来事       | 12    | 2018-07-17 14:20:46 UTC | 2018-07-17 14:20:46 UTC |
| 4  | 3       | 山登りに行きました | 8     | 2018-07-17 14:20:46 UTC | 2018-07-17 14:20:46 UTC |
| 8  | 3       | Ruby on Railsの日  | 9     | 2018-07-17 14:20:46 UTC | 2018-07-17 14:20:46 UTC |
+----+---------+--------------------+-------+-------------------------+-------------------------+
3 rows in set

3つのPostデータが表示されます。

この3つのPostデータのattributesをまとめて更新します。

(3)以下のコードを1行ずつ順番に入力します。

@user.attributes = {
    name:"侍エンジニアさん",
    posts_attributes:[
        {id:3, title: "明日の予定"},
        {id:4, title: "海水浴に行きましょう"},
        {id:8, title: "Ruby on Rails勉強会", month: 10}
    ]
}
@user.posts

実行結果:

+----+---------+----------------------+-------+-------------------------+-------------------------+
| id | user_id | title                | month | created_at              | updated_at              |
+----+---------+----------------------+-------+-------------------------+-------------------------+
| 3  | 3       | 明日の予定           | 12    | 2018-07-17 14:20:46 UTC | 2018-07-17 14:20:46 UTC |
| 4  | 3       | 海水浴に行きましょう | 8     | 2018-07-17 14:20:46 UTC | 2018-07-17 14:20:46 UTC |
| 8  | 3       | Ruby on Rails勉強会  | 10    | 2018-07-17 14:20:46 UTC | 2018-07-17 14:20:46 UTC |
+----+---------+----------------------+-------+-------------------------+-------------------------+
3 rows in set

このコードが予想どおりに動作するのは、app/models/user.rbに、accepts_nested_attributes_for :postsを書いたためです。

では、データベースに保存してから、Railsコンソールを終了しましょう。

(4)以下のコードを1行ずつ順番に入力します。

@user.save
exit

Virtual Attributesを利用する

Virtual Attributesは、データベースに存在せずModelにだけ存在するattributesのことです。

プログラムに存在していた方が便利なattributesを記録しておくために使用します。

ここでは、簡単なVirtual Attributesとして、Userテーブルのnameの文字数を、name_lengthに一時的に記録してみましょう。

(1)app/models/user.rbを以下のように編集します。

変更前:

class User < ApplicationRecord
  has_many :posts
  accepts_nested_attributes_for :posts
end

変更後:

class User < ApplicationRecord
  has_many :posts
  accepts_nested_attributes_for :posts
  attr_accessor :name_length
end

(2)「端末」で以下のコマンドを1行ずつ順番に入力します。

bin/rails console

(3)以下のコードを1行ずつ順番に入力します。

@user = User.find(1)
@user.name_length = @user.name.length

実行結果:

=> 4

@user.name_lengthに値を登録できるのは、app/models/user.rbに、attr_accessor :name_lengthを追加したためです。

たとえば、@user.dummy = 4といったように適当なattributesをでっち上げてデータを登録しようとしても、エラーメッセージが表示されます。

なお、name_lengthについてはデータベースに保存されていないため、Railsコンソールを再起動すると、nilに戻りますので、注意してください。

attr_accessorについては、以下の記事でも触れていますので、興味のある方はご覧いただければと思います。

【Ruby入門】attr_accessorの使い方まとめ
更新日 : 2019年5月15日

まとめ

今回はActive Record(O/Rマッパー)とデータのattributesについて説明し、データのattributesを更新する方法をいくつか紹介しました。

初めに、データを1つ取り出して、そのデータのattributesを更新する方法を紹介しました。

attributesまたはassign_attributesを使うだけでは、データベースに保存されていないため、最後にsaveが必要でした。

また、update_attributesを使うと、データベースに保存されるため、最後のsaveは不要でした。

少し応用的な使いかたとして、入れ子の(has_manyの関係にある)データを、attributes、assign_attributes、またはupdate_attributesを使って一括で更新するためには、accepts_nested_attributes_forを指定する必要があることも説明しましたね。

最後に、データベースには保存する必要がないものの、あると便利なattributes(Virtual Attributesと呼ばれるattributes)を定義する方法を説明しました。

attributesを更新するといっても様々な方法がありますので、最も適切な方法を検討し、コードを書いていきましょう。

それでは。

LINEで送る
Pocket

無料でSEからWebエンジニアへ転職しませんか?



侍エンジニア塾では、完全未経験の方から現在SEだけどプログラミングはやっていないという経験者まで、幅広い方々の人生を好転させるプログラミング指導を行ってきました。SEの方とお話していくなかで、

  • システムエンジニアという職業だけどコードが書けない
  • 事務作業が多くスキルがないため将来が不安
  • スクールに通うと完全未経験者と同じスタートになるからレベルが合わない
という、すでに知識があるSEならではのお悩みがあることに気づきました。そんな方におすすめなのが、弊社の「転職コース 」です。

弊社では、マンツーマンでレッスンを行いますので、現在お持ちの知識レベルからカリキュラムを作成いたします。さらにこちらの転職コースは無料で受講を始められて転職成功でそのまま卒業できるというとてもお得なコースとなっています。

既に知識のあるSEといっても転職は年齢が若いほど受かりやすいため、まずは無料体験レッスンで今の現状や理想の働き方について一緒に考えていきましょう。

まずは無料体験レッスンを予約する

書いた人

侍テック編集部

侍テック編集部

おすすめコンテンツ

あなたにぴったりなプログラミング学習プランを無料で診断!

プログラミング学習の効率を劇的に上げる学習メソッドを解説