-
-
Notifications
You must be signed in to change notification settings - Fork 1
Description
When using a has_many through association on a model that has a custom primary_key, it defaults to using id as the primary key.
has_many :enrollments, inverse_of: :course, dependent: :restrict_with_error
has_many :roster_enrollments, through: :rosters, class_name: "Enrollment", primary_key: :uuid, source: :enrollments
has_many :all_enrollments, class_name: 'Enrollment', union_of: %i[
enrollments
roster_enrollments
]
Course.first.all_enrollments generates sql that looks like
SELECT "enrollment".* FROM "enrollment" WHERE "enrollment"."uuid" IN (SELECT "enrollment"."uuid" FROM ( (SELECT "enrollment"."uuid" FROM "enrollment" WHERE "enrollment"."course_id" = $1) UNION (SELECT "enrollment"."id" FROM "enrollment" INNER JOIN "rosters" ON "enrollment"."roster_id" = "rosters"."id" INNER JOIN "course_rosters" ON "rosters"."id" = "course_rosters"."roster_id" WHERE "course_rosters"."course_id" = $2) ) "enrollment")
Please notice the SELECT "enrollment"."id" in the second UNION.
It appears that this is caused by .select(association.reflection.active_record_primary_key) in scope.rb:17.
Inspecting association.reflection for the direct enrollments looks like this:
{"class_name"=>"Enrollment", "counter_cache_column"=>nil, "inverse_of"=>{"class_name"=>"Course", "counter_cache_column"=>nil, "inverse_of"=>nil, "inverse_which_updates_counter_cache_defined"=>false, "inverse_which_updates_counter_cache"=>nil, "name"=>"course", "scope"=>nil, "options"=>{"optional"=>true}, "active_record"=>"Enrollment", "klass"=>"Course", "plural_name"=>"courses", "join_table"=>nil, "foreign_key"=>"course_id", "association_foreign_key"=>nil, "association_primary_key"=>nil, "inverse_name"=>nil}, "inverse_which_updates_counter_cache_defined"=>false, "inverse_which_updates_counter_cache"=>nil, "name"=>"enrollments", "scope"=>nil, "options"=>{"inverse_of"=>"course", "dependent"=>"restrict_with_error", "autosave"=>true}, "active_record"=>"Course", "klass"=>"Enrollment", "plural_name"=>"enrollments", "join_table"=>nil, "foreign_key"=>nil, "association_foreign_key"=>nil, "association_primary_key"=>nil, "inverse_name"=>"course"}
Inspecting it for the has_many through roster_enrollments looks like this:
{"class_name"=>nil, "counter_cache_column"=>nil, "inverse_of"=>nil, "inverse_which_updates_counter_cache_defined"=>false, "inverse_which_updates_counter_cache"=>nil, "delegate_reflection"=>{"class_name"=>nil, "counter_cache_column"=>nil, "inverse_of"=>nil, "inverse_which_updates_counter_cache_defined"=>false, "inverse_which_updates_counter_cache"=>nil, "name"=>"roster_enrollments", "scope"=>nil, "options"=>{"through"=>"rosters", "class_name"=>"Enrollment", "primary_key"=>"uuid", "source"=>"enrollments"}, "active_record"=>"Course", "klass"=>nil, "plural_name"=>"roster_enrollments", "join_table"=>nil, "foreign_key"=>nil, "association_foreign_key"=>nil, "association_primary_key"=>nil, "inverse_name"=>nil}, "klass"=>nil, "source_reflection_name"=>"enrollments"}
Given that the reflection is not properly reflecting the class type of the association (even though it's specified in the association definition) it looks like active_record_primary_key is just falling back to a default of id.
Simply using .select(primary_key) appears to resolve the issue but I suspect that is only because both unions are returning the same model.