0

I'm changing the inheritance_column value of a base model, which is extended using STI, in an existing app. How can I write a migration in order to make the existing columns conform with the new inheritance_column?

Here's my first attempt:

class MigrateStoryTypes < ActiveRecord::Migration

  def self.up
    Story.all.each { |story|
      new_story_type = story.story_type.camelize + 'Story'
      puts "changing #{story.id}'s story_type from #{story.story_type} to #{new_story_type}"
      story.update_column :story_type, new_story_type
    }
  end

  def self.down
    Story.all.each { |story|
      new_story_type = story.story_type.underscore.gsub /_story/, ''
      puts "changing #{story.id}'s story_type from #{story.story_type} to #{new_story_type}"
      story.update_column :story_type, new_story_type
    }
  end

end

However, this fails with:

ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'clean_slate'. This error is raised because the column ' story_type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Story.inheritance_column to use another column for that information.

Is there a straight-forward way of doing this through ActiveRecord or do I need to use a temporary column, SQL, etc.?

2
  • 1
    So you already have a story_type column that contains underscored versions of the class name (i.e. 'clean_slate') and now you want to move this to STI, use story_type as the STI column, and convert the story_type values to class names? How many story_type values do you currently have? Commented Oct 8, 2016 at 20:27
  • @muistooshort Exactly. I've currently got two story_type values. Commented Oct 8, 2016 at 21:01

1 Answer 1

1

Using models inside migrations is generally a bad idea as the model classes assume they know what the database structure is but migrations are intended to manipulate the database structure. Your error message is just one case of the model classes not be in sync with the database. As soon as Story.all tries to instantiate a model, you get your ActiveRecord::SubclassNotFound STI exception because ActiveRecord expects to find the class name in story_type but you still have your old string types in story_type: you can't fix your database using models until your database is fixed.

I'd recommend that you pretend that your models don't exist at all in migrations, you'll have a better time if you work with the database directly. You only have two story_type values so the SQL is pretty straightforward:

def up
  connection.execute(%q{
    update stories
    set story_type = case story_type
      when 'whatever1' then 'Whatever1Story'
      when 'whatever2' then 'Whatever2Story'
    end
  })
end

There's only two values and you know what they are so don't waste time trying to be clever.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.