1. ホーム
  2. スクリプト・コラム
  3. ルビートピックス

Ruby on Rails:rakeとデータベースのデータ移行作業

2022-01-04 01:43:29

Migrationファイルへのデータ移行をしたことがあるかどうかわかりませんが、ベテランも新人もやったことがあるはずです。実際、うまくいかないわけではないのですが、このやり方は徐々にあなたにとって不要な問題を発生させることになります。

db/migrateフォルダの中身は、一般的にデータベーススキーマの進化について考えられており、新しい開発環境やオンライン環境では、それぞれこのMigrationを経て使用可能なデータベースを構築する必要があるのです。しかし、レガシーデータのマイグレーションコードのような詳細なビジネスコードを読み込んで、しばらくしてデータベースの構造が変わったのにマイグレーションが変わらなかった場合、かつて支援したコードは次第にジャンクコードとなり、環境構築に役立たないだけでなく、rake db:migrate の実行処理も壊してしまうことになるのです。これは新しい環境を構築するためのコストを増加させます。

ですから、正しいやり方は マイグレーションはスキーマ関連のみを担当し、データの詳細には関与しないようにします。 そして、これらのrakeタスクは固定ではなく、時間が経てば非推奨になりますが、他のシステムには対応したくないので、削除することも可能です。しかし、以下のようにRakeを使ってデータ移行を行うこともあるのです。

悪いRakeタスク

# lib/tasks/temporary/users.rake
namespace :users do
 task :set_newsletter => :environment do
  User.all.each do |user|
   if user.confirmed?
    user.receive_newsletter = true
    user.save
   end
  end
 end
end

このタスクはすべてのユーザーに対して反復処理を行うので、データセットが大きい場合にどうなるかを考えてみましょう。
ActiveRecordでデータを更新すると、モデルのバリデーションと作成コールバックメソッドが起動します。
if条件文によるデータの更新が必要かどうかの判断
タスクが何をしているのか視覚的に確認できない、descがないのでrake -Tで見つけられない。
良いレーキタスク

# lib/tasks/temporary/users.rake
namespace :users do
 desc "Update confirmed users to receive newsletter"
 task set_newsletter: :environment do
  users = User.confirmed
  puts "Going to update #{users.count} users"

  ActiveRecord::Base.transaction do
   users.each do |user|
    user.mark_newsletter_received!
    print ". "
   end
  end

  puts " All done now!"
 end
end


desc を使えば、そのタスクが何をしようとしているのかが正確にわかりますし、rake -T でも表示されます。
if文は、スコープによって解決されます。
プログラム実行時の状況を把握するためのカウンタと実行状況表示を導入した
データの変更をトランザクションに入れることで、データの例外やクラッシュによる不整合のための構文が可能になる
最後に付け加えると、特に大きなデータセットの場合、SQL文を直接使った方が簡単で効果的な場合もあり、SQL一つで無駄なループを省くことができる!という点です。また、開発環境に行く前に、Rakeタスクの有効性をテストすることを忘れないでください。