Pythonとサラリーマンと

2020年6月にPythonを始めたサラリーマンのブログです。

Rails Tutorial 第6章を噛み砕く

Rails Tutorial 第6章で学ぶこと

  • ログイン(ユーザー登録)用のテーブルの作り方
  • ユーザ登録用テーブルを作る上で、やっておくことアレコレ

Webサービスでは、ひとりひとりのユーザ情報をデータベース上で管理する必要がある。
そのために早くユーザ登録用のページを作りたい。
しかしユーザ登録ページを作る前に、データベース上にユーザ登録用のテーブルを用意しなければならない。

この記事は、筆者のRails Tutorial 第6章に関する備忘録。
実際に手を動かす時はRails Tutorialの流れに沿って行うこと。
なお、テストに関する記載は省いている。

ユーザ登録で入力してもらう項目

  • name
  • email
  • password
  • password_confirmation

これだけです。この4つの情報を入力してもらう。

validationでユーザが入力する値のルールを決める

上記の情報を入力するときに、空白のまま送信ボタンを押されたら困る。
ちゃんと値が入っているか。短すぎないか。
値入力に関わるルールをvalidationを使えば決められる。
validationで気にすべき項目は以下。

  1. データの有効性
  2. データの存在性(空欄じゃないか)
  3. 長さは適切か
  4. フォーマットは正しいか
  5. データの一意性(既存のデータとダブってないか)

入力値ごとに、これらのvalidationを定めていく。

まずはモデルを作るぜ!

データベースを操作するためには、何はともあれモデルが必要。
railsコマンドでモデルを作成する。

rails generate model User name:string email:string  

「あれ?nameとemailだけ?passwordは?」と気になった方。賢すぎる。
この問題は後に解決される。

rails generate modelを実行すると以下の2つのファイルが作成される。
- app/models/user.rb
- db/migrate/[timestamp]create_users.rb
user.rbファイルにはvalidationを書き込むことができる。
[timestamp]
create_users.rbファイルにはデータベースに作成するテーブル情報を記載する。
なお、rails generate modelを実行すればテーブル情報は自動的に記載されている。
rails db:migrateを実行すればデータベースにテーブルが作成される。
初めてrails db:migrateを実行したときに、db/development.sqlite3というファイルが作られる。
これがデータベースの実体。

validationの書き方

↓これがnameに関するvalidation。

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

↓データはちゃんと入力されているか?空白じゃないか?の確認。

presence: true

↓50文字以上の長さはお断り。の定義。

length: { maximum: 50 }

次にemailのvalidationを見てみよう。

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

↓まずは"正規表現"を使ってemailのフォーマットとは、どのような文字列パターンなのか定義。
*正規表現については後ほど説明する

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

↓ちゃんと定義した通りのemailのフォーマットになってるよな?

format: { with: VALID_EMAIL_REGEX }

↓すでに入力されている値とダブってないよな?大文字小文字に関わらず。

uniqueness: { case_sensitive: false }

次にpasswordに関するvalidation。

has_secure_password
validates :password, presence: true, length: { minimum: 6 }

↓ここで一つのメソッドを使用する。パスワードをハッシュ化しセキュアに管理できる。
*これについても後で詳細説明する。

has_secure_password

このような感じでそれぞれの入力値に関するvalidationを記載し、最終的にuser.rbモデルは以下のような形になる。

class User < ApplicationRecord
  before_save { self.email = email.downcase }
  validates :name, presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },format: { with:VALID_EMAIL_REGEX },uniqueness: { case_sensitive: false }
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
end

↑いきなり

before_save { self.email = email.downcase }

という記述が出てきたが、これはemailを取り込む際に全て小文字に変換する、という指示。
このようにモデルにvalidationを記載することでユーザがデータを入力する際のルールを定義することができる。
これに反した入力があった場合にはエラーで返し、保存させないことができる。

インデックス追加には2つのメリットがある

データにインデックスを追加する方法がある。
インデックスとは本の索引みたいなもの。
テーブルにインデックス項目を作っておけば

  1. データの処理スピードのアップ
  2. データベースの値の一意性の100%確保

を実現できる。
ということで、追加しておきましょう。
「え?すでにvalidationで一意性の定義したやん?」と気づいた方。鋭い。
実はvalidationで規定してても高速で送信ボタンを2回入力されたりしたら、ダブった値が入力されてしまう。
なのでインデックスでしっかり一意性を確保する。
↓userテーブルのemailにindexを追加する。

rails generate migration add_index_to_users_email

すると、
db/migrate/[timestamp]_add_index_to_users_email.rb
このようなファイルが作成される。
↓このファイルに「indexを作りたいです」という内容を記述する。

class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
  def change
    add_index :users, :email, unique: true
  end
end

add_indexというメソッド。unique:tureというオプションを覚えておこう。
なお、rails generate modelを行った時はマイグレーションファイルの中身は自動的に作成されていた。
rails generate migrationではファイルの作成だけで中身は手動で書き込む必要があるので注意。

正規表現

発祥は知らないけど、プログラミング界隈では大量データからある文字列をパターンマッチングするために使用される。
メタ文字.^$[]*+?|()を使用して書く。
メタ文字というのは、正規表現の中ではそれぞれが固有の意味を持っている。
これはこれで勉強した方が良さげ。
↓第6章ではemailの形式が正しいかどうかを確認するために使われている。

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

パスワードはデータベースの中でセキュアに保存

データベースにパスワードが生の文字列で保管されている場合、誰かがデータベースにアクセスしてユーザのパスワードを盗み見することができる。
なので、データベースを覗いてももパスワードが分からないようにしよう。

has_secure_password

これを使えばセキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
また、2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになり、かつ、"存在性"と"値が一致するかどうか"のバリデーションも追加される。
さらにauthenticateメソッドが使えるようになる。 (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド)
しかしuser.rbにこの記述を追加しただけではhas_secure_passwordは使えない。
password_digestという項目をテーブルに追加する必要がある。
↓ということでマイグレーションファイルを作成する。

rails generate migration add_password_digest_to_users password_digest:string

*マイグレーション名は自由。ただし。to_usersとしておけば、usersテーブルへのカラム追加だとRailsが判断してくれる。
↓そしてマイグレーションファイルに「password_digestを追加してくれ」と記載する。

class AddPasswordDigestToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :password_digest, :string
  end
end

最後に

rails db:migrate

で項目の追加が完了。これでhas_secure_passwordが使える状態に。

コントローラでデータを扱う

ここまででUsersテーブルは完成したし、validationによる入力ルールも決められたのでコントローラを使用してデータを扱えるようになった。
基本的な使い方を記載しておこう。
↓新しいuserオブジェクトの作成。

User.new

↓userオブジェクトを作成してデータを格納する。

user = User.new(name:"namae", email:"mailaddress@yahoo.co.jp")

↓userオブジェクトに格納したデータの有効性を確認。(validationに反していないか)

user.valid?

↓valid?でfalseになった時のエラーの内容を確認。

user.error.full_messages

↓userオブジェクトに格納したデータをデータベースに格納する。

user.save

↓これを使えばuser.newとuser.saveを一発で完了。

User.create(name:"namae", email:"mailaddress@yahoo.co.jp")

↓データの保存完了後、idによりデータの検索。

user.find(1)

↓emailによってデータの検索。

user.find_by(email:"mailaddress@yahoo.co.jp")

↓一番先頭のデータを呼び出す。

user.first

以上。Rails Tutorial 第6章を噛み砕いた。