Использование рейк-задачи для переноса данных является чрезвычайно рискованной идеей. Пара причин, чтобы не делать этого:
Даже если вам удастся выяснить, является ли ваша задача грабли закончена или нет, ваша миграция по-прежнему будет помечена как завершенная, и вы не сможете его повторить. Только способ создания исключения в вашей миграции.
Нет, вы не сможете откатить эту миграцию. Если после выполнения миграции завершена задача rake, rollback попытается добавить уже существующий столбец.
Настройка вашей базы данных с нуля новыми разработчиками станет болезненной, поскольку они должны будут знать, какие задачи грабли должны выполняться, когда. Не говоря уже о том, что rake db:migrate
выполняет все миграции.
Вы загрязняя список задач грабель с не многоразовыми задачами
кажется, что вы делаете, это просто обычный перенос данных, поэтому все вещи сделаны вашей передней задача должны быть на самом деле частью вашей миграции. Это позволит даже сделать обратную миграцию данных (в большинстве случаев).
Обратите внимание, что миграция данных не такая простая, как регулярные миграции только для схемы. Поскольку ваша миграция должна быть полностью независимой от вашего кода (поскольку они должны работать в будущем, даже если мигрированная модель полностью удалена из вашей кодовой базы), поэтому обычной практикой является переопределение моделей, которые вы собираетесь использовать в своих миграциях (только бит, необходимый для миграции). Это не так просто, как это звучит, к сожалению, и, честно говоря, я все еще ищу идеальное решение. Лучшее, что я видел до сих пор просто (я предполагаю, что paid_by
раньше строка, и вы изменили его paid_by_id
, который ссылается на пользователя):
class YOURMIGRATIONNAME < ActiveRecord::Migration
class Transaction < ActiveRecord::Base
belongs_to :paid_by, class_name: "User"
end
class User < ActiveRecord::Base
end
def up
add_column :transaction, :paid_by_id, :integer
Transaction.transaction do # for speed
Transaction.find_each do |t|
t.paid_by_id = User.find_by(username: t[:paid_by])
t.save! # Always banged save in migration!
end
end
remove_column :paid_by
end
def down
add_column :transaction, :paid_by, :string
Transactions.transaction do
Transaction.find_each do |t|
t[:paid_by] = t.paid_by && t.paid_by.username
t.save!
end
end
remove_column :transactions, :paid_by_id
end
Единственный недостаток использования кода выше, что это не сработает, если какая-либо из этих моделей использует STI (я однажды совершил эту ошибку, потребовалось некоторое время, чтобы выяснить, что не так). Работа заключается в том, чтобы определить ее за пределами класса миграции, но тогда эти классы доступны во всех миграциях и могут быть затронуты вашим фактическим кодом модели (особенно в производстве, когда все модели предварительно загружены). Короче говоря, миграция данных с STI - это то, что я сейчас изучаю.
Это пахнет много. Почему миграция зависит от задачи рейка? Ваша схема никогда не должна зависеть от вашего кода. За что отвечает упомянутая задача рейка? – BroiSatse
Вы не можете знать, была ли она выполнена, если вы не храните эту информацию где-нибудь. Как файл или БД, которые вы пишете из задачи, а затем проверьте миграцию. Я предполагаю, что задача рейка необходима для переноса данных, прежде чем вы сможете удалить столбец? Возможно, вы можете проверить это в чистом SQL (то есть, если столбец не является нулевым или похожим)? И только потом удалите столбец?Это сделало бы вашу миграцию «самосохраненной» –
@BroiSatse Я удаляю столбец в миграции, но перед удалением этого столбца мне нужно скопировать значения из этого столбца и заполнить новый столбец. Вот почему мне нужно убедиться, что задача была запущена до того, как я запустил миграцию. –