rails
rails copied to clipboard
association with `query_constraints: []` always returns empty collection when included
Steps to reproduce
With query_constraints: []
one can create a custom query for an association, for example:
class BodyPart < ActiveRecord::Base
end
class Human < ActiveRecord::Base
has_many :body_parts, -> { where(for: 'human') }, query_constraints: []
end
This results in the following correct sql:
Human.last.body_parts.to_sql
# => SELECT "body_parts".* FROM "body_parts" WHERE "body_parts"."for" = 'human'
So far so good, but it always returns an empty collection when adding an includes(:body_parts)
:
Human.create!
BodyPart.create!(for: 'human')
Human.last.body_parts
# => [<instance of BodyPart>]
# correct
Human.includes(:body_parts).last.body_parts
# => []
# wrong
Is this a misuse of query_constraints
? Should it break on query_constraints: []
?
Or is this a bug including/eager_loading associations in case there are no query_constraints?
Background
The idea is to have custom associations that can be preloaded. The example above is very simple and doesn't depend on the the attributes at all (so the result is the same for every human), but it could be more complicated with a custom where
query string etc. that takes into account the attributes of the model.
Test template
Template
# frozen_string_literal: true
require 'bundler/inline'
gemfile(true) do
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem 'rails', github: 'rails/rails', branch: 'main'
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 :humen, id: :uuid, force: true do |t|
end
create_table :body_parts, id: :uuid, force: true do |t|
t.string :for
end
end
class BodyPart < ActiveRecord::Base
end
class Human < ActiveRecord::Base
has_many :body_parts, -> { where(for: 'human') }, query_constraints: []
end
class BugTest < Minitest::Test
def test_without_include
Human.delete_all
BodyPart.delete_all
body_part_for_human = BodyPart.create!(id: SecureRandom.uuid, for: :human)
BodyPart.create!(id: SecureRandom.uuid, for: :dog)
Human.create!(id: SecureRandom.uuid)
assert Human.all.first.body_parts == [body_part_for_human]
end
def test_with_include
Human.delete_all
BodyPart.delete_all
body_part_for_human = BodyPart.create!(id: SecureRandom.uuid, for: :human)
BodyPart.create!(id: SecureRandom.uuid, for: :dog)
Human.create!(id: SecureRandom.uuid)
assert Human.all.includes(:body_parts).first.body_parts == [body_part_for_human]
end
end
References
might ref #50068
System configuration
Rails version: main
Ruby version: ruby 3.2.2 (2023-03-30 revision e51014f9c0) +YJIT [arm64-darwin22]