らくだ🐫にもできるRailsチュートリアル|6.1

6章でやる事
ユーザー用のデータモデルの作成と、データを保存する手段の確保
→ユーザー登録を出来る様にする

6.1 Userモデル

Railsでは、データモデルとして扱うデフォルトのデータ構造のことをモデル (Model) と呼びます (1.3.3で言うMVCのMのことです)

railsの標準のデータ構造→model
こんな雰囲気でいいのかな?

railsではデータベースを使ってデータを長期保存している
→データベースとやり取りをしてデータオブジェクトの作成・保存・検索を行うメソッドが用意されている
→マイグレーション (Migration)機能により、データの機能をRubyで記述できる

マイグレーションとはなんぞ

migration→移行・移動 的な言葉
railsで言うマイグレーションとは
→データベースにテーブルを作成したり、既存のテーブルにカラムを追加するなどの変更を加えたりできる機能
→使用するデータベースがMySQLでもSQLiteでも対応できる
→専用のファイルを作成して実行する(詳細は後程)

リレーショナルデータベースとはなんぞ

関係データベース(リレーショナルデータベース)
考えないで感じましょう😊(ひとまず)

作業を始める前にブランチを切る
→$ git checkout -b modeling-users

6.1.1 データベースの移行

nameとemailの2つの属性を持つUserモデルを作成する
→保存したいデータ
 id(数字)・name(短い文字列)・email(短い文字列)

→対応するデータモデル案
(カラム名):(データ型)
id:integer 数列(整数)
name:string 文字列
email:string 文字列
これに沿ってモデルを作成する

#controller名では複数形だったけどmodelは単数形
$ rails generate model User name:string email:string
  
Running via Spring preloader in process 4752
      invoke  active_record
                #マイグレーションファイル
      create    db/migrate/20190708020426_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml

マイグレーションファイルとはなんぞ

データベースに与える変更を定義したchangeメソッドの集まり
ファイル名の頭にタイムスタンプが付く

class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t| 
      t.string :name     #stringの型を持つnameカラム
      t.string :email    #stringの型を持つemailカラム

      t.timestamps       #created_atとupdated_atを作成する
    end
  end
end

created_atとupdated_atとはなんぞ

それぞれ、そのデータの作成時刻・更新時刻を自動的に記録したタイムスタンプ

マジックカラム(MagicColumn)とはなんぞ

マジックカラム→予約語→プログラムでもともと「予約(設定)」されている単語
使われる予定がある単語なので変数名や関数名に定義できない

実際生成されるUserモデル

usersテーブル
(カラム名):(データ型)
id:integer
name:string
email:string
created_at:datetime
updated_at:datetime

マイグレーションの適用

マイグレーションはコマンドを入力することで実行され、データベースに変更を加える

$ rails db:migrate

初めてdb:migrateを実行した時にdb/development.sqlite3という名前のファイルが生成される
→SQLite5データベースの実体
(DB Browser for SQLiteと言うツールを使用してデータベースの構造を見ることが出来る)
(本文参照にて)

演習

  1. Railsはdb/ディレクトリの中にあるschema.rbというファイルを使っています。これはデータベースの構造 (スキーマ (schema) と呼びます) を追跡するために使われます。さて、あなたの環境にあるdb/schema.rbの内容を調べ、その内容とマイグレーションファイル (リスト 6.2) の内容を比べてみてください。
  2. ほぼすべてのマイグレーションは、元に戻すことが可能です (少なくとも本チュートリアルにおいてはすべてのマイグレーションを元に戻すことができます)。元に戻すことを「ロールバック (rollback)と呼び、Railsではdb:rollbackというコマンドで実現できます。
    上のコマンドを実行後、db/schema.rbの内容を調べてみて、ロールバックが成功したかどうか確認してみてください (コラム 3.1ではマイグレーションに関する他のテクニックもまとめているので、参考にしてみてください)。上のコマンドでは、データベースからusersテーブルを削除するためにdrop_tableコマンドを内部で呼び出しています。これがうまくいくのは、drop_tableとcreate_tableがそれぞれ対応していることをchangeメソッドが知っているからです。この対応関係を知っているため、ロールバック用の逆方向のマイグレーションを簡単に実現することができるのです。なお、あるカラムを削除するような不可逆なマイグレーションの場合は、changeメソッドの代わりに、upとdownのメソッドを別々に定義する必要があります。詳細については、Railsガイドの「Active Record マイグレーション」を参照してください。
  3. もう一度rails db:migrateコマンドを実行し、db/schema.rbの内容が元に戻ったことを確認してください。

1.

class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end
・
・
・
#(マイグレーションファイルのタイムスタンプ)
ActiveRecord::Schema.define(version: 20190708020426) do

  create_table "users", force: :cascade do |t|
    t.string "name"
    t.string "email"
    #t.timestampsに含まれるマジックカラムが展開(?)されている(nullはfalse)
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false

  end

end

内容はほぼ同じ
force: :cascade→外部キーが適切であればスキーマが再読み込みできるようにするための記述
外部キー→他のテーブルと結びつけるためのキー(ざっくり)
スキーマ→データベースの構造(ざっくり)
(このあたりの用語を念頭に感じ取る!)
2.3.は動作確認のみにて省略

6.1.2 modelファイル

Modelファイルの確認

演習

  1. Railsコンソールを開き、User.newでUserクラスのオブジェクトが生成されること、そしてそのオブジェクトがApplicationRecordを継承していることを確認してみてください (ヒント: 4.4.4で紹介したテクニックを使ってみてください)。
  2. 同様にして、ApplicationRecordがActiveRecord::Baseを継承していることについて確認してみてください。
#1.
>> @user.class
=> User(id: integer, name: string, email: string, created_at: datetime, updated_at: datetime)
>> @user.class.superclass
=> ApplicationRecord(abstract)
#2.
>> @user.class.superclass.superclass
=> ActiveRecord::Base

6.1.3 ユーザーオブジェクトを作成する

sandboxモード →データベースに変更を加えたくないときに使用

>> User.new
   #引数が無いのですべての値がnilで帰ってくる
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>
>> user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
   #インスタンスをuserに代入 引数に値も渡す
=> #<User id: nil, name: "Michael Hartl", email: "mhartl@example.com", created_at: nil, updated_at: nil>
>> user.valid?
   #オブジェクトが有効かどうかを確認するメソッド→.valid?
=> true  #有効なのでtrue
>> user.save
   #saveメソッドを呼び出してオブジェクトを保存
=> true   #保存できたのでtrue
>> user   #保存したuserオブジェクトを呼び出す
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-07-10 09:28:16", updated_at: "2019-07-10 09:28:16">
   #保存したことによりidと作成と更新のタイムスタンプが入力された

   #ドット記法を用いて属性にアクセスできる
>> user.name
=> "Michael Hartl"
>> user.email
=> "mhartl@example.com"
>> user.updated_at
=> Wed, 10 Jul 2019 09:28:16 UTC +00:00

   #modelの生成と保存を同時に行う方法もある↓
   #trueかfalseの代わりにオブジェクト自身が返ってくる
>> User.create(name: "A Nother", email: "another@example.org")
=> #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2019-07-10 09:38:57", updated_at: "2019-07-10 09:38:57">
   #変数に代入することも出来る
>> foo = User.create
=> #<User id: 3, name: nil, email: nil, created_at: "2019-07-10 09:39:47", updated_at: "2019-07-10 09:39:47">

   #データの削除と、削除したと言う事の保存を同時に行うことも出来る
>> foo.destroy
=> #<User id: 3, name: nil, email: nil, created_at: "2019-07-10 09:39:47", updated_at: "2019-07-10 09:39:47">
   #削除したオブジェクトはメモリ上には残っている
>> foo
=> #<User id: 3, name: nil, email: nil, created_at: "2019-07-10 09:39:47", updated_at: "2019-07-10 09:39:47">

オブジェクトが本当に削除されているか、などの検索方法は次項で

演習

  1. user.nameとuser.emailが、どちらもStringクラスのインスタンスであることを確認してみてください。
  2. created_atとupdated_atは、どのクラスのインスタンスでしょう
#1.#2.それぞれ.classで確認
>> user.name.class
=> String
>> user.email.class
=> String
>> user.created_at.class
=> ActiveSupport::TimeWithZone
>> user.created_at.class.superclass
=> Object
>> user.updated_at.class
=> ActiveSupport::TimeWithZone
>> user.updated_at.class.superclass
=> Object

6.1.4 ユーザーオブジェクトを検索する

様々な検索方法

>> User.find(1)
   #Userモデルから引数の(値や配列)のレコードを取得
   #()は省略できる奴
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-07-10 09:28:16", updated_at: "2019-07-10 09:28:16">
>> User.find(3)
   #上記で削除したレコード(id: 3)を呼び出そうとしたので例外が発生
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=3)

>> User.find_by(email: "mhartl@example.com")
   #Userモデルから、指定した条件の最初の1件を取得する
   #()は省略できる奴
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-07-10 09:28:16", updated_at: "2019-07-10 09:28:16">

>> User.first
  #Userモデルの最初の1件を取得
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-07-10 09:28:16", updated_at: "2019-07-10 09:28:16">

>> User.all
   #Userモデルの全てのデータを取得
=> #<ActiveRecord::Relation [#<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-07-10 09:28:16", updated_at: "2019-07-10 09:28:16">, #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2019-07-10 09:38:57", updated_at: "2019-07-10 09:38:57">]

演習

  1. nameを使ってユーザーオブジェクトを検索してみてください。また、 find_by_nameメソッドが使えることも確認してみてください (古いRailsアプリケーションでは、古いタイプのfind_byをよく見かけることでしょう)。
  2. 実用的な目的のため、User.allはまるで配列のように扱うことができますが、実際には配列ではありません。User.allで生成されるオブジェクトを調べ、ArrayクラスではなくUser::ActiveRecord_Relationクラスであることを確認してみてください。
  3. User.allに対してlengthメソッドを呼び出すと、その長さを求められることを確認してみてください (4.2.3)。
#1.()は!省略!出来る!!(でもあった方がわかりやすくないです?)
>> user.name
=> "Michael Hartl"
>> User.find_by name: "Michael Hartl"
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-07-10 09:28:16", updated_at: "2019-07-10 09:28:16">
>> User.find_by_name "Michael Hartl"
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-07-10 09:28:16", updated_at: "2019-07-10 09:28:16">
#2. .classで調べる
>> User.all.class
=> User::ActiveRecord_Relation

#3. .lengthで調べる(現状データは2個なので合ってる)
>> User.all.length
=> 2

6.1.5 ユーザーオブジェクトを更新する

>> user
   #userの中身を確認
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2019-07-12 06:37:54", updated_at: "2019-07-12 06:37:54">
>> user.email = "mhartl@example.net"
   #user.emailに新しい情報を代入
=> "mhartl@example.net"
>> user.save 
   #情報を保存
=> true
>> user
    #userの中身を確認(emailが変更されている updated_atも変更されている)
=> #<User id: 1, name: "Michael Hartl", email: <"mhartl@example.net", created_at: "2019-07-12 06:37:54", updated_at: "2019-07-12 06:39:16">

saveで保存をせずに再読み込みをすると変更が取り消される
(DBから情報を読み込むため)

>> user.email
   #userのemailを呼び出す
=> "mhartl@example.net"
>> user.email = "foo@bar.com"
   #新しい情報を代入
=> "foo@bar.com"
>> user.reload.email
   #上書きしていない状態で再読み込み
=> "mhartl@example.net"
   #DBから情報を読み込むので元のemailになる

update_attributesメソッド

属性のハッシュを受け取り、検証に成功した場合に更新と保存を同時に行うメソッド

>> user.update_attributes(name: "The Dude", email: "dude@abides.org")
=> true    #成功するとtrueが返ってくる
>> user.name
=> "The Dude"
>> user.email
=> "dude@abides.org"
#update_attributesは一つでも検証に失敗すると呼び出せない
#特定の属性のみを更新したい場合はupdate_attribute(←単数形)を使う
>> user.update_attribute(:name, "El Duderino")
#nameのみを更新する
=> true
>> user.name
=> "El Duderino"

演習

  1. userオブジェクトへの代入を使ってname属性を使って更新し、saveで保存してみてください。
  2. 今度はupdate_attributesを使って、email属性を更新および保存してみてください。
  3. 同様にして、マジックカラムであるcreated_atも直接更新できることを確認してみてください。ヒント: 更新するときは「1.year.ago」を使うと便利です。これはRails流の時間指定の1つで、現在の時刻から1年前の時間を算出してくれます。
>> user   #現状を確認
=> #<User id: 1, name: "El Duderino", email: "dude@abides.org", created_at: "2019-07-12 06:37:54", updated_at: "2019-07-12 07:20:41">
#1.
>> user.name = "rakuda"
=> "rakuda"
>> user.save
=> true
#2.
>> user.update_attribute(:email, "rakuda@mail.com")                            
=> true
#3.
>> user.update_attribute(:created_at, 1.year.ago)
=> true
#データの更新を確認してみる
>> user
=> #<User id: 1, name: "rakuda", email: "rakuda@mail.com", created_at: "2018-07-12 07:27:24", updated_at: "2019-07-12 07:27:24">

まとめとか感想

Userモデルを作成して、
その後はコンソールでデータの作り方などを色々確認。
4章の時と似たような感じでしたね。

らくだ🐫にもできるRailsチュートリアルとは

「ド」が付く素人のらくだ🐫が勉強するRailsチュートリアルの学習記録です。
自分用に記録していますが、お役に立つことがあれば幸いです。

調べたとはいえらくだ🐫なりの解釈や説明が含まれます。間違っている部分もあるかと思います。そんな所は教えて頂けますと幸いなのですが、このブログにはコメント機能がありません💧お手数おかけしますがTwitterなどでご連絡いただければ幸いです