databaseruby-on-rails-4sqlitemigrationsti

Rails new migrations not updating old data


I have a model called Article. This model has attribute called duration which is a string. The duration can be morning, afternoon, evening and night.

For example, article object looks like this:

#<Article id: 1, created_at: "2015-09-22 08:00:08", updated_at: "2015-09-22 08:00:08", duration: "morning">

Since the duration has similar properties, I create a base class called duration and the inherited classes as morning, afternoon, evening and night.

In a duration there can be many articles. But a single article can have only one duration. So, I have has_many and belongs_to association:

app/model/duration.rb
class Duration < ActiveRecord::Base
  has_many :articles
end

app/model/article.rb
class Article < ActiveRecord::Base
  belongs_to :duration
end

Other inherited classes are:

app/model/duration/morning.rb
class Morning < Duration
end

and so on for afternoon.rb, evening.rb and night.rb.

I already have the migration to create durations table. To add the field type to duration, I have a migration called add_type_to_duration.rb

AddTypeToDuration < ActiveRecord::Migration
  def change
    add_column :durations, :type, :string
  end 
end

I have another migration file called add_duration_ref_to_articles.rb to add reference

class AddDurationRefToArticles < ActiveRecord::Migration
  def change
    add_reference :articles, :duration, index:true
  end
end

I have another migration to create the new durations in add_initial_durations.rb

class AddInitialDurations < ActiveRecord::Migration
  def change
    Morning.create
    Afternoon.create
    Evening.create
    Night.create 
  end
end

Now I want to update the old data to adapt according to new migrations. So, I have another migration called update_articles_to_have_duration.rb

class UpdateArticlesToHaveDuration < ActiveRecord::Migration
  def change
    morning = Duration.find_by_type("Morning")
    Article.where(duration: "morning").find_each do |article|
      article.update!(duration_id: morning.id)
    end
  end
end

Now when I run the migrations all the articles, that had duration = morning, now have duration_id = nil. However, when I run the last migration again with rake db:migrate:redo step:1, then articles have correct duration_id. I think somehow the migration is not run properly. However, I don't get any error while running them. Can anyone please let me know what am I doing wrong here?

Thanks for the help.


Solution

  • As you've said that duration_id is set properly when running the migration second time, the only reason why it's not working on the first go might be that the migrations are not running sequentially as you've shown.

    Migration files have timestamps on them and they run oldest first when you do rake db:migrate. See the timestamps of the migration file to ensure that they're ordered as you want them.

    ❯ rake db:migrate:status                                                                                                 
    
    database: ~/blog/db/development.sqlite3
    
     Status   Migration ID    Migration Name
    --------------------------------------------------
       up     20150907015616  Create articles
      down    20150907031746  Create comments
      down    20150909034614  Devise create users
    

    You can use above command to see the Migration Id and copy it to run the migrations one after another to verify my assumption with following command:

    ❯ bin/rake db:migrate VERSION=20150907015616 VERSION=20150907031746 VERSION=20150909034614
    

    You'll have to re-order(better to delete and recreate) the migration files or transpose its contents if the migration files do not follow the chronological order of timestamps.