Rails4
seed-fu
マスタデータ
paranoia

seed-fuでマスタデータを管理していたら困った事があった

More than 1 year has passed since last update.

seed-fuは便利

seed-fu便利です。お世話になってます。いいと思います。ありがとうございます。
seed-fu(GitHub)

ここ参考にして使ってます。

スプレッドシートでマスタデータ作って、csvとしてdb/fixturesに何個か配置して、下みたいなタスクを1ファイルだけ作って管理してました。このやり方だとタスクは1ファイルだけなので、ファイルが複数あるとアルファベット順で実行されるとかいうウザさもなくて良いですね。でもテーブル多かったらそうも言ってられないかもですね。

import_master.rb
require 'csv'

CSV.read('db/fixtures/' + Rails.env + '/model1.csv', headers: :first_row).each do |row|
  Model1.seed do |s|
    s.id = row['id']
    s.name = row['name']
    s.category = row['category']
    s.deleted_at = row['deleted_at']
  end
end

CSV.read('db/fixtures/' + Rails.env + '/model2.csv', headers: :first_row).each do |row|
  Model2.seed do |s|
    s.id = row['id']
    s.name = row['name']
    s.desc = row['desc']
    s.image = row['image']
    s.deleted_at = row['deleted_at']
  end
end

・・・・・・

これで初回投入時であれcsvマスタデータ修正して再度実行時であれ、 rake db:seed_fu により解決できる訳です。そう思って小気味よく開発してたんです。

Mysql2::Error: Duplicate entry '{id}' for key 'PRIMARY': INSERT INTO 〜〜

なんでやねん。
再実行しても、主キー(id)同じデータが存在したら更新してくれるって書いてあるやん。なんでや。

答え:論理削除してるから

seed-fu duplicateとかでgoogle先生に聞いて同じ目にあってる人いないか調べて誰もいなくてショック受けたりしつつ、色々試してみると、deleted_atカラムに値入ってたり入ってなかったりでエラーになったりならなかったりしてる事が分かった(ちなみにparanoiaで使ってる論理削除フラグ)。paranoiaはActiveRecordのメソッド書き換えたりしてるから、DBのデータで論理削除されてるデータはseed-fu実行時にも無いと判断され、無理やりデータ突っ込みに行ってmysqlから怒られてたんですね。

なのでしょうがないので、タスクの方で論理削除されるかどうか見るようにして対応しました。

import_master.rb
require 'csv'

def logical_deleted?(model, row)
  record = model.with_deleted.find_by(id: row['id'])
  record.present? && record.deleted_at.present?
end

CSV.read('db/fixtures/' + Rails.env + '/model1.csv', headers: :first_row).each do |row|
  next if logical_deleted?(Model1, row)
  Model1.seed do |s|
    s.id = row['id']
    s.name = row['name']
    s.category = row['category']
    s.deleted_at = row['deleted_at']
  end
end

CSV.read('db/fixtures/' + Rails.env + '/model2.csv', headers: :first_row).each do |row|
  next if logical_deleted?(Model2, row)
  Model2.seed do |s|
    s.id = row['id']
    s.name = row['name']
    s.desc = row['desc']
    s.image = row['image']
    s.deleted_at = row['deleted_at']
  end
end

・・・・・・

seed-fuと言うより、論理削除を併用してる場合の問題でした。