Rails Tutorial 6章 編集中

ここで主に扱うのは、ユーザーのデータモデルの作成と、ユーザーを保存する手段の確保。



Userモデル

$ rails g model User name:string email:string

※コントローラー名には複数形、モデル名には単数形

すると、こんなマイグレーションファイルが作成される

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

      t.timestamps
    end
  end
end

このファイルを

$ rails db:migrate

で実行。そうすることで、この変更データがDBに反映される。

ちなみに、マイグレーションを元に戻すには、

$ rails db:rollback

初めてdb:migrateが実行されると、 db/development.sqlite3 という名前のファイルが生成され、このファイルからDBの構造を確認できる。



modelファイル

app/models/user.rb
class User < ApplicationRecord
end

つまり、User < ApplicationRecord < ActiveRecord::Base

では、ActiveRecord::Baseでは何ができるのか?

1、オブジェクトの作成
例えば、

>> User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>

オブジェクトの有効性

>> user.valid?
true

オブジェクトをDBに保存

>>user.save
 ~~~~
=>true

saveメソッドは、成功すればtrueを、失敗すればfalseを返す。(timestanpが更新されたり)

また、

>>User.create(name: "A Nother", email: "another@example.org")

では、true/falseではなく、オブジェクト自身を返す。

2、オブジェクトの検索

User.find(1)
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com",
created_at: "2016-05-23 19:05:58", updated_at: "2016-05-23 19:05:58">

DBに存在しないオブジェクトを検索した場合、findメソッドは例外を発生。

>> User.find(3)
ActiveRecord::RecordNotFound: Couldn't find User with ID=3

findメソッド以外にも、特定の属性でユーザーを検索する方法も

>> User.find_by(email: "mhartl@example.com")
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com",
created_at: "2016-05-23 19:05:58", updated_at: "2016-05-23 19:05:58">

また、こんなのも

>>User.first
>>User.all

3、オブジェクトの更新

属性のハッシュを受け取り、成功時には更新と保存を続けて同時に行う。しかし、検証に失敗すると、update_attributesの呼び出しは失敗する。

>>user.update_attributes(name: ~~, email: ~~)
=>true

そこで、update_attributeを使えば検証を回避できる。(特定の属性のみを更新)

>>user.update_attribute(name: ~~)
=>true

ユーザーを検証
app/models/user.rb

<存在性のバリデーション>
presence: t/f

validates :name, presence: true
validates :email, presence: true
#またはvalidates(:~~~, presence: true)

<長さ>
length: { }

validates :name, presence: true, length: { maximum: 50 }
validates :email, presence: true, length: { maximum: 255 }

<emailのフォーマット>
正規表現というものを使ってメールアドレスフォーマットを指定。詳しくはまだ手をつけないほうがいいらしい。

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }

<emailの一意性を検証>
これを加えるだけ。

uniqueness: true

通常、メールアドレスは大文字小文字が区別されないので、

uniqueness: { case_sensitive: false }

と書き換える。
trueをcase_sensitive: falseに置き換えただけだが、Railsは:uniquenessをtrueと判断する。



しかし、DBのレベルでは一意性を保証していないため、素早くリクエストが複数送信された際に同じ値を持つユーザーレコードが作成されてしまう。

そこで、DBにインデックスというものを追加。
(本でいう索引みたいなものらしい)
これによって、DBに一意性の設定が可能&検索効率の向上

データモデリングの変換が必要なので、

$ rails g migration add_index_to_users_email

オプション unique :true で一意性を強制

db/migrate/[timestamp]_add_index_to_users_email.rb
def change
 add_index :users, :email, unique: true
end
rails db:migrate


また、データベースによっては常に大文字と小文字を区別するとは限らないため、アプリケーション側でメールアドレスをすべて小文字にすることによって対策。

app/models/user.rb
 before_save { self.email = email.downcase }

※ Userモデルの中では右式のselfを省略できる





ユーザーが有効かどうかは valid?メソッドで確認でき、検証に失敗した時はfalse、全てのバリデーションに通った時はtrueを返す。

>>User.valid?
=>true







これの有効性・存在性のテスト
 → setup にてあらかじめユーザーを作成

test/models/user_test.rb
def setup
 @user = User.new(name: "Example User", email: "user@example.com")
end

def test "should be valid" do
 assert @user.valid?
end

def test "name should be present" do
 @user.name = "  "
 assert_not @user.valid?
end

def test "email should be present" do
 @user.email = "     "
 assert_not @user.valid?
end

#長さの検証
test "name should not be too long" do
 @user.name = "a" * 51
 assert_not @user.valid?
end

test "email should not be too long" do
 @user.email = "a" * 244 + "@example.com"
 assert_not @user.valid?
end