Ruby
Rails
seed-fu

seed-fuでバリデーションがスキップされる

More than 1 year has passed since last update.

seed-fuを使って初期データを作成していたのですが、ドハマリをしたのでメモっておきます。現象としては、特にエラーも出ずおかしなデータが作られているといったものです。

検証してみる

モデルのバリデーションはこんな感じでかけています。

user.rb
validates :name, presence: true

試しに、初期データ作成を以下のようにコメントアウトします。
作成時にバリデーションに引っかかることを期待して、nameもコメントアウト。

db/fixtures/users.rb
User.seed do |obj|
  #obj.name = "ユーザー1"
  #obj.email = "test+100@gmail.com"
  #obj.password = 'password'
  #obj.password_confirmation = 'password'
end
u = User.first
u.update(name: 'test')

初期データ投入を実行します。
何事もなく実行されました。

» bundle exec rake db:seed_fu FILTER=users

== Filtering seed files against regexp: /users/

== Seed from /Users/hoge/projects/hoge/db/fixtures/users.rb
 - User {}

え!?っと思い、ログを確認するも、特にエラーは出ていません。

log/development.log
[2017-05-02 15:12:03] (pida=4863) DEBUG -- : [db:seed_fu ] started
[2017-05-02 15:12:03] (pida=4863) DEBUG -- :    (0.4ms)  begin transaction
[2017-05-02 15:12:03] (pida=4863) DEBUG -- :   User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" IS NULL LIMIT ?  [["LIMIT", 1]]
[2017-05-02 15:12:03] (pida=4863) DEBUG -- :   SQL (3.1ms)  INSERT INTO "users" ("confirmation_token", "confirmation_sent_at", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["confirmation_token", "XoT-7mK4K8RCsvzJcH4_"], ["confirmation_sent_at", 2017-05-02 06:12:03 UTC], ["created_at", 2017-05-02 06:12:03 UTC], ["updated_at", 2017-05-02 06:12:03 UTC]]
[2017-05-02 15:12:03] (pida=4863) DEBUG -- :    (1.7ms)  commit transaction
[2017-05-02 15:12:03] (pida=4863) DEBUG -- : [db:seed_fu ] finished

データもちゃんと入っている。。。ちゃんとと言うか、本当は入ってほしくないんです。え!?バリデーションどこいっちゃたの?

1||||||0|||||KTsFoLHadSQf3wvGAJsZ||2017-05-02 06:27:21.799427||2017-05-02 06:27:21.799210|2017-05-02 06:27:21.799210||||0|

しかし、レコードは作成されているものの、その後で実行されているupdateによるnameは空のまま。updateはこけている模様。しかし何もエラーなし。

update時に、例外を吐くようにupdate!に、てみる。

db/fixtures/user.rb
User.seed do |obj|
  #obj.name = "ユーザー1"
  #obj.email = "test+100@gmail.com"
  #obj.password = 'password'
  #obj.password_confirmation = 'password'
end
u = User.first
u.update!(name: 'test')

同じく、実行すると標準出力にエラーがでました。
ログをみても、ロールバックされていることがわかる。
ロールバックが働いているため、レコード自身も作成されていませんでした。

log/development.log

[2017-05-02 15:18:28] (pida=5180) DEBUG -- : [db:seed_fu ] started
[2017-05-02 15:18:28] (pida=5180) DEBUG -- :    (0.4ms)  begin transaction
[2017-05-02 15:18:28] (pida=5180) DEBUG -- :   User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" IS NULL LIMIT ?  [["LIMIT", 1]]
[2017-05-02 15:18:28] (pida=5180) DEBUG -- :   SQL (0.4ms)  INSERT INTO "users" ("confirmation_token", "confirmation_sent_at", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["confirmation_token", "CnY6D24dv7zj_sRzo8Zf"], ["confirmation_sent_at", 2017-05-02 06:18:28 UTC], ["created_at", 2017-05-02 06:18:28 UTC], ["updated_at", 2017-05-02 06:18:28 UTC]]
[2017-05-02 15:18:28] (pida=5180) DEBUG -- :   User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."deleted_at" IS NULL ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
[2017-05-02 15:18:28] (pida=5180) DEBUG -- :    (1.3ms)  rollback transaction

実際にアップデートされていると思い込んでいたデータが一向に更新されず、プログラムのバグかと思い、永遠にはまりました。

まとめると、

  1. Model.seedでデータを作る際には、モデルで設定しているバリデーションに関係なくレコードが作成される。
  2. ActiveRecordで取得したデータ(インスタンス)に対して、updateなどをかけるとモデルに設定しているバリデーションが働く。当然と言っちゃ当然か。

注意すること

  1. モデルでバリデーションを設定していても、Seedだと直接ガンガンレコードがインサートされてしまう。
  2. なんとなく使うのやめよう。。。ツールにはツールのポリシーがあって使う側も勉強しないとアカン、、、

課題

  1. seed-fuでデータを作成する際にバリデーションでチェックさせる方法はないのか?

結構、違和感の残る検証結果になってしまいました。もしそもそも使い方が間違ってるぞ!などのご指摘があれば、ご教授いただけると幸いです。