testdouble/good-migrations

Polymorphic Associations

Opened this issue · 4 comments

Hey, I'm having trouble with good_migrations in a migration where one of the classes has a polymorphic association. Just wondering if anyone has hit this issue before, or has any tips?

Here's a rough example of what it looks like:

class MigrateWidgets < ActiveRecord::Migration[5.2]
  class Widget < ActiveRecord::Base
    belongs_to :poly, polymorphic: true
  end
  class Foo < ActiveRecord::Base; end
  class Bar < ActiveRecord::Base; end

  def up
    # Example 1; GoodMigrations::LoadError (attempting to load app/models/foo.rb)
    Widget.where(poly_type: 'Foo').includes(:poly).find_each do |widget|
      # ...
    end

    # Example 2; GoodMigrations::LoadError (attempting to load app/models/bar.rb)
    Widget.where(poly_type: 'Bar').last.poly
  end
end

As a workaround, using require to explicitly load the classes that are used in the polymorphic associations works, but obviously it's not ideal...

A bit late to the party, but if you don't need the STI (ex, you just want to edit some fields), you can do this:

class MigrateWidgets < ActiveRecord::Migration[5.2]
  class Widget < ActiveRecord::Base
    self.inheritance_column = 'this_column_doesnt_exist'
  end
  # ...
end

This way, Rails doesn't detect the STI for your custom model, so it doesn't try to load sub-models from the top-level.

Clever! @MaxLap would you be interested in sending a PR to the README to include this pro-tip?

Sorry, I just realized my tip doesn't apply here. My tip is for dealing with STI, not with polymorphic associations.

@searls I don't know if you still want this in the README, but since it's so short, I wouldn't know where to put it or how to contextualize it. I think you are better placed to the the change. Here is a bit of an explanation that you could build upon:

Dealing with STI

When redefining a model inside of a migration class, a problem happens if the model uses STI (single table inheritance): Rails will try to load the actual model from the type column. The load is done from the top-level namespace, so you can't define more models for each sub-models in the migration class, they won't be found.

Depending on your situation, you may not need STI at all for your migration. In that case, a solution is to set the STI column (normally type) to a column that doesn't exist, so that Rails doesn't try to do STI.

class ChangeSomething < ActiveRecord::Migration
  class User < ActiveRecord::Base
    self.inheritance_column = 'this_column_doesnt_exist'
  end
end