迁移在生产中失败,但在开发中按预期进行。
... # everything to
20120709174326_add_subject_to_campaigns.rb # here runs fine.
20120711001125_set_default_value_for_publishable_in_newsletter.rb # <- this fails.
20120711010818_set_default_value_for_publishable_in_contents.rb
20120711010855_set_default_value_for_published_in_editions.rb
20120711191427_add_newsletter_date_to_newsletters.rb
20120711194230_rename_cm_campaign_sent_at_in_campaigns.rb
迁移错误如下所示:
-bash> heroku run rake db:migrate --remote staging
Running rake db:migrate attached to terminal... up, run.1
== SetDefaultValueForPublishableInNewsletter: migrating ======================
-- change_column(:newsletters, :publishable, :boolean, {:default=>false})
-> 0.1554s
rake aborted!
An error has occurred, this and all later migrations canceled:
undefined method `newsletter_date' for #<Newsletter:0x00000005961bd0>
关键是,newsletter_date直到以后才添加,所以为什么它这么快失败/提及呢? 它在开发中运行得非常好。
事实证明迁移失败( 20120711001125_set_default_value_for_publishable_in_newsletter.rb
)...
def up
change_column :newsletters, :publishable, :boolean, default: false
Newsletter.scoped.where('publishable is NULL').each do |n|
n.publishable = false
n.save
end
end
... 使用模型 Newsletter
,并在保存之前运行验证(特别是对newsletter_date
的presence
验证)...
validates :newsletter_date, presence: true
...但newsletter_date
还不存在,因为它( newsletter_date
列)要到以后才添加( 20120711191427_add_newsletter_date_to_newsletters.rb
上面列表中倒数第二个迁移):
class AddNewsletterDateToNewsletters < ActiveRecord::Migration
def change
add_column :newsletters, :newsletter_date, :date
end
end
解决方案是:(1)在开发中,将迁移回滚到失败的迁移到失败的迁移; (2) 重命名添加了列的迁移,将其时间戳更改为迁移失败之前。 (3) 运行迁移; (4) 更新 git; (5) 推送至Heroku; (6)在生产中, 进行迁移; (7) 重启 Heroku进程。
bundle exec rake db:rollback # five times in this case
mv db/migrate/20120711191427_add_newsletter_date_to_newsletters.rb db/migrate/20120711001025_add_newsletter_date_to_newsletters.rb
bundle exec rake db:migrate
git add db/migrate/20120711001025_add_newsletter_date_to_newsletters.rb
git rm db/migrate/20120711191427_add_newsletter_date_to_newsletters.rb
git commit -m "changed migration timestamp to fix migration order issue"
git push staging master
heroku run rake db:migrate --remote staging
heroku restart --remote staging
它在开发中起作用的原因是,在运行为newsletters
表中的publishable
列设置默认值的迁移时, newsletter_date
列和模型验证尚不存在,因此这不是问题。 等到全部投入生产时,更新的代码就使newsletter_date
方法和相关的验证成为现实,但是由于迁移是按时间戳顺序在现有代码上运行的,因此可能存在比数据库准备好的新代码。