LoginSignup
1
0

More than 1 year has passed since last update.

Rails初学者によるRailsチュートリアル学習記録⑧ 第6章

Posted at

目次

1. はじめに

  • この記事は、Rails初学者の工業大学三年生がRailsチュートリアルの学習記録を
    つけるための記事です。
  • 筆者自体がRailsやWebについて知識が少ないので、内容の解釈などに
    間違いがある可能性があります。(その時はコメントで指摘してくださると助かります!)
  • Railsチュートリアル内ではRailsの内容以外にも、gitでのバージョン管理やHerokuを使ったデプロイも
    学習しますが、gitに関しては既に私が学習済みのため学習記録には記述しません。
  • 演習の記録も省略します。

2. 第6章の概要

この章では、ユーザーのデータモデルを定義し、データベースにデータを保存する方法について学びます。
モデルが作成できたら、登録ページを準備してフォームのバリデーションやパスワードをセキュアに扱う機能を実装していきます。

  1. ユーザーモデルの作成
    1. Userデータモデルの設計
    2. ユーザーオブジェクトをデータベースに保存する
    3. ユーザーオブジェクトを更新する
    4. ユーザーを検証する
  2. インデックスの作成
  3. セキュアなパスワードを追加する

3. 学習内容

1. ユーザーモデルの作成

1-1. Userデータモデルの設計

この章で作成するユーザーモデルは4章で作成したような、nameとemailという属性を持つユーザーモデルです。
モデルを作成するためにrails generate model User name:string email:stringコマンドを実行します。
このコマンドによりマイグレーションファイルが作成され、そこにRubyでデータベースの構造を記述していくことになります。
↓ 作成されたマイグレーションファイル

db/migrate/[timestamp]_create_users.rb
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email
      t.timestamps
    end
  end
end

このファイルはデータベースに与える変更を定義したchangeメソッドの集まりで、
このファイルではコマンド実行時に指定したstring型のname, emailという2つの属性が定義されている。
t.timestampsという行は自動的に追加される属性で、ユーザーが作成されたり更新された時にその時刻を自動的に記録する属性である。
↓Userのデータモデル

属性 データ型
id integer
name string
email string
created_at datetime
updated_at datetime

そして、rails db:migrateを実行することでマイグレーションファイルに定義された
データベースへの変更を、データベースに反映できます。

1-2. ユーザオブジェクトをデータベースに保存する

マイグレーションによって作成したユーザーオブジェクトをデータベースに保存する準備ができたので、
railsコンソールでユーザーオブジェクトを作成・保存してみます。

コンソールでuser.newを使用してユーザーオブジェクトを作成してもデータベースには保存されていないため、
コンソールを終了するとオブジェクトは消えてしまいます。
作成したオブジェクトをデータベースに保存するには、saveメソッドを用います。
saveメソッドは成功したらtrue、失敗したらfalseを返します。

このコマンドを実行するとデータベースにオブジェクトが保存されますが、
今回のように試しに保存してデータベースへの変更は取り消したいという時には
コンソールをサンドボックスモードで起動するrails console --sandboxを実行すると良いです。

また、saveメソッドでオブジェクトの保存を行うにはnewメソッドでオブジェクトの生成をする必要があるのですが、
オブジェクトの生成とデータベースへの保存を同時に行うにはcreateメソッドを使用します。

データベースに保存したユーザーを検索するには、いくつかの方法があるので順に紹介していきます。
①idを指定して検索する
ユーザーのidを指定して検索するにはfindメソッドを使用します。
例: User.find(3) → idが3のユーザーを検索する

②特定の属性で検索する
id以外の特定のユーザーで検索するにはfind_byメソッドを使用します。
例: User.find_by(email: "abc123@example.com") → email属性がabc123@example.comのユーザーを検索する

③データベースの最初のユーザーを検索する
データベースの最初のユーザーを取得するにはfirstメソッドを使用します。
例: User.first → データベースの最初のユーザーを検索する

④すべてのユーザーを取得する
データベースの全てのユーザーを取得するにはallメソッドを使用します。
例: User.all → データベースの全てのユーザーを取得する
返ってくるオブジェクトはActive-Record::Relationというクラスで、各オブジェクトが配列としてまとめられています。

1-3. ユーザオブジェクトを更新する

一度データベースに保存した情報を更新するには2つの方法があります。
1つ目はユーザーオブジェクトを取得して、属性を代入する方法です。
ユーザーオブジェクトを検索した後、user.email = "aiueo@example.com"のように
属性に対して値を代入し直してからsaveメソッドを実行することでオブジェクトを更新できます。

2つ目はupdateメソッドを使用する方法です。
user.update(name: "The Dude", email: "dude@abides.org")
updateメソッドは属性のハッシュを受けとって成功時には更新と保存を同時に行います。
オブジェクトにパスワードが実装されると更新時に保存を要求するようになり、
正しいパスワードを渡さないと検証で失敗するようになります。
このメソッドでは全ての属性を渡す必要があるが、特定の属性のみを更新したい場合は、
update_attributeメソッドを使用します。
user.update_attribute(:name, "El Duderino")
このメソッドは検証を回避することができます。

1-4. ユーザを検証する

作成したUserモデルはnameとemailという属性があり、これらはどんな値でも取ることができます。
しかし、実際にアプリケーションを運用するには
nameとemailは空ではいけない、emailはメールアドレスのフォーマットに従っていなければならないといった制限が必要です。
この制限のことを検証(Validation)と呼びます。

①存在性の検証
まずは最も基本的な、「値が存在するか?」を検証していきます。
最初に存在性の検証が正しくできているかを確認するテストをtest/models/user_test.rbというファイルに記述していきます。

テストファイルにはsetupという特殊なメソッドを使用してユーザーオブジェクトを生成、@userに代入します。
setupメソッド内に書かれた処理はテストの前に実行されるので、全てのテスト内で@userというインスタンス変数を使用することができます。

存在性のテストは@userのnameとemailに空白を設定した後に、@userの有効性を確認します。
テストは以下のように書きます。

test/models/user_test.rb
require 'test_helper'
class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

  test "should be valid" do # 初めに@userが有効かテストする
    assert @user.valid?
  end

  test "name should be present" do # nameの検証ができているかのテスト
    @user.name = ""
    assert_not @user.valid?
  end

  test "email should be present" do # emailの検証ができているかのテスト
    @user.email = " "
    assert_not @user.valid?
  end
end

上記のテストで assert と assert_not が使用されているのは、
最初の有効性のテストはオブジェクトが有効になっている → valid?がtrueを返せばOKなのに対して、
存在性の2つのテストは、値が空白になることでオブジェクトが無効になる → validがfalseを返せばOKだからassert_notを使用しています。

これでテストが完成し、この時点では検証が実装されていないのでこのテストは失敗します。
実際の検証は以下のようにapp/models/user.rbに記述します。

app/models/user.rb
class User < ApplicationRecord
  validates :name, presence: true
  validates :email, presence: true
end

validates :属性名, presence: trueと記述することで存在性が検証できます。
このように検証の実装は望ましくない値を代入して、オブジェクトが無効になるかを確認するテストを書き、
その後に検証を実装するというテスト駆動開発の流れが適しています。
下にこの章で実装する完全なを検証を載せて、簡単な説明のコメントを添えます。

app/models/users.tb
class User < ApplicationRecord
  before_save {self.email = email.downcase} # 保存する前に実行される emailを小文字にする

  validates :name, presence: true, length: {maximum: 50} # nameの検証 最大50文字で値が存在するか

  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i # emailフォーマットの正規表現 詳細は割愛

 # 以下emailの検証  
  validates :email,  presence: true, length: {maximum: 255},   # 最大255文字
                    format: { with: VALID_EMAIL_REGEX },       # フォーマットは上記の正規表現に従う 
                    uniqueness: true                           # 一意性を持つ

# 以下パスワードの検証 詳細は後述
  has_secure_password
  validates :password, presence: true, length: {minimum: 6}
end

2. インデックスの作成

インデックスとはデータベース上のデータの検索速度を速めたり、データベース上のデータの制約を追加するものです。
特定の属性に対して追加するもので、今回はemail属性にインデックスを追加します。
そのためのコマンドはrails generate migration add_index_to_users_emailです。
最後のadd_index_to_users_emailはファイル名となります。

db/migrate/[timestamp]_add_index_to_users_email.rb
class AddIndexToUsersEmail < ActiveRecord::Migration[6.0]
  def change
    add_index :users, :email, unique: true
  end
end

add_indexといメソッドでusersテーブルのemailカラムにインデックスを追加しています。
unique: trueはemailカラムに一意性を強制しています。

2. セキュアなパスワードを追加する

パスワードは入力された文字列をそのままデータベースに保存するのではなく、
文字列をハッシュ化したものを保存します。

このようなセキュアなパスワードの実装はhas_seure_passwordというメソッドでほとんどが済みます。
このメソッドをUserモデルで呼び出すと、以下の機能が使えるようになります。
1. ハッシュ化したパスワードを、password_digestという属性に保存する
2. password と password_confirmation という2つの仮想的な属性が使えて、存在性と2つの値が一致するかの検証が追加される
3. authenticateメソッドという引数とパスワードが一致するかを確かめるメソッド

メソッドの追加のほかにやることは、Userモデルへのpassword_digest属性の追加と、
bcryptというハッシュ関数を使用するためのgemの追加です。

それらができたら、users.rbの最後に以下の二行を追加して、パスワードを使用した認証機能の完成です。

app/models/users.tb
  has_secure_password
  validates :password, presence: true, length: {minimum: 6} # パスワードの最小文字数を6文字にする

4. 終わりに

第6章から本格的なアプリケーションの機能追加が始まって内容が難しくなってきた印象です。
内容が濃いので、記事に書く内容を選別しており特にテストの内容に関しては今回載せた検証などの、
特に重要度が高かったり、内容の理解が重要な部分のみを記述していく予定です。
記事作成に3~4時間くらいかかっているのですが、読み返してみると数分で見れる内容になっているのが、
見やすいようで、あれだけ頑張ってこれくらいの文量かとなって複雑な気持ちになってます:(

これ以降も難しい内容が終わりまで続くと思うので、進むスピードが落ちそうですが挫折しないことを第一に頑張りますb

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0