12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Rails】バルクインサート・バルクアップデートがvalidationにひっかかった時の対処法

Posted at

##バルクインサートとは
INSERTするとき、一個一個保存していると、それに応じてsqlも発行されてしまいます。

User.each do |user|
  user.name = "hoge"
  user.save
end

そこで、一括でINSERT してDBに対する負担を減らそうとするときに使うのがバルクインサート。

users = []
User.each do |user|
  user.name = "hoge"
  users << user
end
User.import users

これでクエリを一個で済ますことができます。

##バルクアップデート

以下のように、更新させる値を指定してアップデートすることも可能.

users = []
User.each do |user|
  user.name = "hoge"
  users << user
end
User.import users, on_duplicate_key_update: [:name, :age]

便利なものですが、 今回,validationに引っかかり保存できなくてハマったので共有します。
##バリデーションで引っかかっている

単純にUserモデルと、それに属するPostモデルがあるとする.


#  name   :string
#  age    :integer
User < < ActiveRecord::Base
  has_many :posts
end
#  title   :string
#  content :text
User < < ActiveRecord::Base
  belongs_to :user
end

今回はUserと、それに紐づくPostの値を更新するために以下のように実装するわけだが、ここに落とし穴がある。

users = []
posts = []
User.each do |user|
  user.name = "taro"
  user.age  = 20
  users << user
  post = user.posts.build(title: "テスト", content: "テストテキスト")
  posts << post
end
User.import! users, on_duplicate_key_update: [:name, :age]
Post.import posts

実行


Parent.import! users, on_duplicate_key_update: [:name, :age]
ActiveRecord::RecordInvalid: バリデーションに失敗しました。 Postsの値が不正です。

userが持っていたpostモデルも同時に作っていたので、そのpostモデルのバリデーションに引っかかってしまった。

とりあえず、postが保存されればuserも保存されることを確認する。

user = users.first
post = user.posts.first
post.save
true
user.save
true

やはり、子モデル(post)が正常に保存されていない状態ではuserは保存できない。
今回のミスとして、一つのモデルに対して、二つのモデルを二重に保存させようとしていたことがわかる。
つまり、どちらか一方を正常に保存させてからでないと、どちらか一方のバリデーションに引っかかり保存できないということ。

なのでuserを保存させてからpostのみバルクインサートさせるという仕様に変えた。


posts = []
User.each do |user|
  user.name = "taro"
  user.age  = 20
  user.save
  post = user.posts.build(title: "テスト", content: "テストテキスト")
  posts << post
end
Post.import posts

##validateオプションを使う
特にvalidatenを外すことに抵抗がなければ、以下のようにvalidate: false を使うことでimportに成功させることもできる。

User.import users, on_duplicate_key_update: [:name, :age], validate: false

最初からこれを使えばいいわけだが, これでは子モデル(post)にユニーク制限があった場合でも全て保存されてしまうので注意したいところ。

12
12
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
12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?