【パーフェクト Ruby on Rails】を読む - その6

SQLite3ではnot null制約を付与しつつカラムのデフォルト値を指定しない場合、カラムの追加でエラーになってしまいます。 例)

# NG
class AddNicknameToUsers < ActiveRecord::Migration[6.0]
    def change
        add_column :users, :nickname, :string, null: false
    end
end

# OK
class AddNicknameToUsers < ActiveRecord::Migration[6.0]
    def change
        add_column :users, :nickname, :string, null: false, default: 'sample'
    end
end

# OK
class AddNicknameToUsers < ActiveRecord::Migration[6.0]
    def change
        add_column :users, :nickname, :string
        change_column :users, :nickname, :string, null: false
    end
end

パーフェクト Ruby on Rails p60の例での説明

2-2-2

# 2xxxxxxxxxxxxx_add_publisher_id_to_books.rb
略
def change
  add_reference :books, :publisher, foreign_key: true #rails g migration時にreferencesをつけた事により外部キーであるforeign_keyが生成されており、この行でpublisher_idが作られます。(注:publisher_idとこの行で書いてしまうとpublisher_id_idとなってしまいます。)

  change_column :books, :publisher_id, :integer, null: false #ここで1行目に生成されたpublisher_idにnull: falseをつけることでnot null制約を付与しています。

rails c 内でのreloadとは

reloadメソッドとは、データベースに保存されているもともとのレコードをリロードすることです。 以下、検証結果です。

irb(main):021:0> user = User.new(last_name: '田中',
 first_name: '花子',
 password: 'password',
 password_confirmation: 'password',
 email: 'sample@sample.com' )

=> #<User id: nil,
 email: "sample@sample.com",
 crypted_password: nil,
 salt: nil,
 created_at: nil,
 updated_at: nil,
 last_name: "田中",
 first_name: "花子",
 avatar_image: nil,
 reset_password_token: nil,
 reset_password_token_expires_at: nil,
 reset_password_email_sent_at: nil,
 access_count_to_reset_password_page: 0,
 role: "general">

irb(main):022:0> user.reload
Traceback (most recent call last):
        1: from (irb):22
ActiveRecord::RecordNotFound (Couldn't find User without an ID')

irb(main):023:0> user.save
   (0.1ms)  begin transaction
  User Exists (0.6ms)  SELECT  1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "sample@sample.com"], ["LIMIT", 1]]
  User Create (1.0ms)  INSERT INTO "users" ("email", "crypted_password", "salt", "created_at", "updated_at", "last_name", "first_name") VALUES (?, ?, ?, ?, ?, ?, ?)  [["email", "sample@sample.com"], ["crypted_password", "$2a$10$FFT.hUH/PZI5ae5ajqeLUe1kM.H0AWS/mJV0.ZIKJo1ev7N1JcDMC"], ["salt", "FNGVQWhJLc3N-nHWvs9H"], ["created_at", "2021-09-03 16:04:23.608038"], ["updated_at", "2021-09-03 16:04:23.608038"], ["last_name", "田中"], ["first_name", "花子"]]
   (1.1ms)  commit transaction
=> true
irb(main):024:0> user.reload
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 15], ["LIMIT", 1]]
=> #<User id: 15, email: "sample@sample.com", crypted_password: "$2a$10$FFT.hUH/PZI5ae5ajqeLUe1kM.H0AWS/mJV0.ZIKJo1...", salt: "FNGVQWhJLc3N-nHWvs9H", created_at: "2021-09-03 07:04:23", updated_at: "2021-09-03 07:04:23", last_name: "田中", first_name: "花子", avatar_image: nil, reset_password_token: nil, reset_password_token_expires_at: nil, reset_password_email_sent_at: nil, access_count_to_reset_password_page: 0, role: "general">
irb(main):025:0> 

SQLiteの出力モード

  • line : 1行ずつ表示。
  • html : html形式で表示。
  • tabs : 各列をタブ区切りで表示。
  • csv : 各列をカンマ区切りで表示。

2-2-3

結論 「バリデーション + データベースでの制約」どっちも必要、安全!!」 Race Conditionが起きないように!!

Race Condition レースコンディションとは、複数の処理が同じデータに同時にアクセスした場合に、機能停止など予期しない処理結果が生じてしまうことです。 つまり 同時にアクセスなどしたときにバリデーションをすり抜ける可能性があるため二重ロックの意味でもバリデーションだけでなくnot null制約もつけるべきです。 バリデーションはDBに渡る前のActiveRecordの段階で制御し、not nullはDBに渡ってからDBで制御されます。

バリデーションヘルパー

ActiveRecord/ActiveModelの機能を使用したバリデーションです。(presence: trueなど)

validates :price, numericality: { greater_than_or_equal_to: 0 }の内訳

numerucality は数値であること、オプションでゼロ以上、偶数かどうかなどを検査します。greater_than_or_equal_to: 0 はゼロ以上であること >= と同義です。

バリデーションでpresence: trueが無く、DBでNot NULL制約がある場合DBには保存はされない。
エラーメッセージを表示したい場合はインスタンス.errors.messageまたはインスタンス.errors.full_messageを記述すると良い。(errorsはメソッド)


メソッドに!をつけるかつけないか

!をつけないとバリデーション失敗時に例外を起こさない !をつけると失敗時に例外を起こす

nullとnil

nilrubyの言語。nullの意味。 -nilでもメソッドのエラーを出したくない時は&.ぼっち演算子を使う

参考文献

reloadメソッド
https://k-koh.hatenablog.com/entry/2020/12/28/214557
Race Condition
https://enterprisezine.jp/securityonline/word/detail/%E3%83%AC%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%B3%E3%83%87%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3