Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveRecord: preload for has_many through with source_type provided fails if has_many through association without source_type was specified in preload earlier. #49355

Open
EnotPoloskun opened this issue Sep 22, 2023 · 2 comments

Comments

@EnotPoloskun
Copy link

Steps to reproduce

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem "rails", "7.0.8"
  gem "sqlite3"
end

require "active_record"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new($stdout)

ActiveRecord::Schema.define do
  create_table "invoices", force: :cascade, &:timestamps

  create_table "invoice_payments", force: :cascade do |t|
    t.integer "invoice_id"
    t.integer "source_id"
    t.string "source_type"
  end

  create_table "checks", force: :cascade, &:timestamps

  create_table "charges", force: :cascade do |t|
    t.integer "card_id"
  end

  create_table "cards", force: :cascade, &:timestamps
end

class Invoice < ActiveRecord::Base
  has_many :invoice_payments
  has_many :charges, through: :invoice_payments, source: :source, source_type: "Charge"
end

class InvoicePayment < ActiveRecord::Base
  belongs_to :invoice
  belongs_to :source, polymorphic: true
end

class Check < ActiveRecord::Base
end

class Charge < ActiveRecord::Base
  belongs_to :card
end

class Card < ActiveRecord::Base
end

class BugTest < Minitest::Test
  def test_preload
    invoice = Invoice.create!
    check = Check.create!
    card = Card.create!
    charge = Charge.create!(card: card)
    InvoicePayment.create!(invoice: invoice, source: charge)
    InvoicePayment.create!(invoice: invoice, source: check)

    assert_equal invoice, Invoice.preload(:invoice_payments).first # works
    assert_equal invoice, Invoice.preload(:charges).first # works
    assert_equal invoice, Invoice.preload(:invoice_payments, :charges).first # works
    assert_equal invoice, Invoice.preload(:charges, :invoice_payments).first # works
    assert_equal invoice, Invoice.preload({ charges: :card }, :invoice_payments).first # works
    assert_equal invoice, Invoice.preload(:invoice_payments, { charges: :card }).first # fails
  end
end

Expected behavior

Invoice.preload(:invoice_payments, { charges: :card }).first

Should return first invoice with preloaded associations provided in preload same way how

Invoice.preload({ charges: :card }, :invoice_payments).first

does. Order inside preload must not matter.

Actual behavior

Following error is raised:

ActiveRecord::AssociationNotFoundError: Association named 'card' was not found on Check; perhaps you misspelled it?

System configuration

Rails version: 7.0.8
Ruby version: 3.2.2

@EnotPoloskun EnotPoloskun changed the title ActiveRecord: Preload for has_many through with source_type provided fails if has_many through association without source type was specified in preload earlier. ActiveRecord: preload for has_many through with source_type provided fails if has_many through association without source_type was specified in preload earlier. Sep 22, 2023
@EnotPoloskun
Copy link
Author

EnotPoloskun commented Sep 23, 2023

I believe that potential workaround for that issue is defining scoped association for has_many through to use. E.g in my example Invoice model should be changed to following

class Invoice < ActiveRecord::Base
  has_many :invoice_payments
  has_many :charge_invoice_payments, -> { where(source_type: "Charge") }, class_name: "InvoicePayment"
  has_many :charges, through: :charge_invoice_payments, source: :source, source_type: "Charge"
end

and tests will pass.

@paulreece
Copy link
Contributor

paulreece commented Sep 23, 2023

I can confirm the error occurs from the reproduction script on the current development branch for 7.x.

Possibly related to:
49197
49084

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants