rails: ActiveRecord::HasManyThroughOrderError

I have a has_one association between 3 models, but it have an error that says “ActionView::Template::Error (Cannot have a has_many :through association ‘Policy#intermediary’ which goes through ‘Policy#invoice’ before the through association is defined.)”

class Policy < ApplicationRecord

    self.table_name = "gipi_polbasic"
    self.primary_key = "policy_id"

    has_one :invoice
    has_one :intermediary, through: :invoice, foreign_key: :intrmdry_intm_no
class Intermediary < ApplicationRecord
    self.table_name = "giis_intermediary"
    self.primary_key = "intm_no"

    has_one :invoice, foreign_key: :intrmdry_intm_no
    belongs_to :policy, foreign_key: :policy_id
class Invoice < ApplicationRecord
    self.table_name = "gipi_comm_invoice"
    self.primary_key = "intrmdry_intm_no"

    belongs_to :policy, foreign_key: :policy_id
    belongs_to :intermediary, foreign_key: :intrmdry_intm_no

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 12
  • Comments: 33 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Apparently, it meant literally order. I was having this problem, and I’m not sure if everyone having this error with the same reason, but if you define a class like this, it will give you HasManyThroughOrderError.

class User < ApplicationRecord
  has_many :groups, -> { distinct }, through: :memberships
  has_many :memberships, dependent: :destroy
end

Defining like this, solved my problem.

class User < ApplicationRecord
  has_many :memberships, dependent: :destroy
  has_many :groups, -> { distinct }, through: :memberships
end

its working on rails 5.0.3 but when i updated to rails 5.1.1 this error appeared

I’ve got the same issue and as @aoozdemir, I solved it by setting the has_many of the join table before the has_many trough

Here is the reason for the change https://github.com/rails/rails/pull/27485. Like you can see it is by design. We want 5.1 raise for a thing that was “fine” in 5.0. It was never fine, the fact that it worked in 5.0 for some case doesn’t means that defining the through association before the source association worked fine for all the cases. Because of that it is safer to always define the source association before the through association so now we are enforcing this order to make explicit to the users that there are consequences when defining in a different order.

As for defining the same association twice (if you’re talking about :orgs has_many programs and :orgs has_many :through associations) this is a very common pattern that works in many applications, so am unsure why you’d consider this a bug (maybe it’s “un-5.1’s-y”?).

I don’t know how that works. If you have two different association with the same name which one you think it is going to be used when you do species.orgs, the first or the second?

Old issue but still relevant for some people. Here’s a rspec that will reveal all your current ActiveRecord::HasManyThroughOrderError

# frozen_string_literal: true

describe 'ActiveRecord::HasManyThroughOrderError' do
  describe '#has_many and has_one through' do
    Rails.application.eager_load!
    %i(has_one has_many).each do |association_kind|
      has_through_associations = ApplicationRecord.descendants.reject(&:abstract_class?).each_with_object({}) do |ar_class, result|
        has_through_associations = ar_class.reflect_on_all_associations(association_kind).select do |association|
          association.options.key? :through
        end
        result[ar_class] = has_through_associations.map(&:name) if has_through_associations.any?
      end
      has_through_associations.each do |ar_class, association_names|
        test_class = ar_class.new
        association_names.each do |association_name|
          context "loading #{ar_class}. #{association_name} should not raise an HasManyThroughOrderError" do
            it 'should be a success' do
              if association_kind == :has_one
                expect { test_class.send(association_name.to_sym) }.not_to raise_error
              else
                expect { test_class.send(association_name.to_sym).first }.not_to raise_error
              end
            end
          end
        end
      end
    end
  end
end

This is a same issue with #31068. I think that redefining association in a STI subclass is a reasonable usage. I’ve fixed it in 652258e41a882acccdb9a3ce211dbf356e738b28.

Would be nice if these errors came up on booting rails. Relying on your test coverage to trigger the correct scopes has me a little nervous about the upgrade. Is there any way of loading all the Models and making sure the order is correct?

There are two different issues here.

One is defining the same association twice, this is raising HasManyThroughOrderError but it should not, this is a bug since it should raise a different exception.

The other is defining the :though association before the source association, this should raise HasManyThroughOrderError since there are implications in the behavior when defining the :through association before the source association. This is not a bug. The guide doesn’t mention order of associations because the exception exists exactly to avoid people to define them in the wrong order.

I also stumbled across this issue when upgrading from 5.0.4 to 5.1.1 and it threw me for a loop. I am using Concerns and have one module named Accountable which declares a has_ many :through. I include this concern in a couple of other models. Under 5.0.4 there is no issue, but 5.1.1 complains about this. The fix so far has been to move the offending belongs_to to the line before including the concern in this model.

  belongs_to :company
  include Accountable

This was very confusing until I ran across this thread and realized order of declaration was important. I try to follow the Ruby Style Guide so I always put includes and extend statements before associations.

@cjmblue05 Make sure you don’t have another redundant has_one :intermediary in your Policy model. Got the same error after update.

@aoozdemir @Blaked84 Thanks guys, doing the following:

class Species < ActiveRecord::Base
  has_many :programs
  has_many :assessments
  
  has_many :orgs, :through => :programs
  has_many :orgs, :through => :assessments

does eliminate the one error (though why it should work in 5.0.3 and not 5.1.1 is beyond me.)

Sadly though, I am still getting the TypeError: can't cast Hash on @program.species in irb which makes me think there is some boog in the associations with 5.1.1 (as mentioned, downgrading to 5.0.3 has everything working as expected.). It does seem subtle and insidious though… especially if it somehow causes an order error on having all the has_manys defined before the has_many => throughs (as you’ll note in my original code, I was defining the has_many for each through association before the actual has_many through, so it’s a bit weird it threw the errors.

thanks for the assistance though! If there is anything you guys think I can do to help provide more info to run this thing down, please let me know.