Sadiksha Gautam
Sadiksha Gautam

Reputation: 5152

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.

Upvotes: 0

Views: 1407

Answers (2)

smunozmo
smunozmo

Reputation: 1

I had the same issue and solved it using MyModel.reset_column_information

class UpdateArticlesToHaveDuration < ActiveRecord::Migration
  def change
    Article.reset_column_information

    morning = Duration.find_by_type("Morning")
    Article.where(duration: "morning").find_each do |article|
      article.update!(duration_id: morning.id)
    end
  end
end

It resets all the cached information about columns, which will cause them to be reloaded on the next request.

The most common usage pattern for this method is probably in a migration, when just after creating a table you want to populate it with some default values

For more details, you can refer to the documentation.

Upvotes: 0

zoras
zoras

Reputation: 953

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.

Upvotes: 3

Related Questions