Edited at

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でデータを作成する際にバリデーションでチェックさせる方法はないのか?

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