Rails6にしたので、insert_all
メソッドが使えるようになったので、意気揚々とgem activerecord-importを削除しようと修正していってたのだけれど、思ったより難しそうだったので、一旦止めることにした。
activerecord-importの削除が難しい理由
recursiveオプションが便利で、それを使っていた
activerecord-importには再起的に関連データをインポートできる仕組みがあって、それを外していくことの影響度が大きくてテストが落ちまくるようになりました…。gemを削除するためだけ調査するには時間がかかりすぎるので棚上げすることにしました。
問題
insert_all
メソッドはHashを対象とするので、モデルの配列を渡してもNGでした。そのため、activerecord-importのimportメソッドをinsert_allに変えただけではダメ。また、insert_allには以下のような問題がありました。
- idにnilが設定されていると落ちる
- created_at, updated_atが自動的に入らない
解決できるもの
上記の問題は、attributes_without_id
メソッドをApplicationRecordに作成することで大体問題なく動いてくれました。
class ApplicationRecord < ActiveRecord::Base self.abstract_class = true # これを定義 def attributes_without_id self.attributes.except('id').tap do |hash| hash[:created_at] ||= Time.zone.now hash[:updated_at] ||= Time.zone.now end end end
これで、activerecord-importのメソッドは以下のように置換できます。
books = FactoryBot.build_list(:book, 100) # activerecord-importの場合 Book.import(books) # insert_allの場合 Book.insert_all(books.map(&:attributes_without_id)) # もしくは、attributes_for_listにすれば、モデルのオブジェクトの生成が不要に。 # 新規に使っていくならこちらがベストかな? books = FactoryBot.attributes_for_list(:book, 100) Book.insert_all(books)
最後のやつが最も効率よくデータを作られるはず…(試してない)
まとめ
insert_allは速いけれど、関連データがたくさんあるケースだと厳しい…。無理に削除せずに共存していくことにします。