Skip to content

Commit 5cbdfba

Browse files
committed
Fix associations associated with a CPK model by id attribute
Given a model associated with a composite primary key by `id` attribute, for example: ```ruby Order.primary_key = [:shop_id, :id] OrderAgreement.primary_key = :id Order.has_many :order_agreements, primary_key: :id ``` Accessing the association should perform queries using the `id` attribute value and not the `id` as Order's composite primary key. ```ruby order = Order.last # => #<Order id: 1, shop_id: 2> order.order_agreements.to_a ```
1 parent e11ebc0 commit 5cbdfba

File tree

7 files changed

+41
-3
lines changed

7 files changed

+41
-3
lines changed

activerecord/lib/active_record/associations/association_scope.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def last_chain_scope(scope, reflection, owner)
6262
table = reflection.aliased_table
6363
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
6464
primary_key_foreign_key_pairs.each do |join_key, foreign_key|
65-
value = transform_value(owner[foreign_key])
65+
value = transform_value(owner._read_attribute(foreign_key))
6666
scope = apply_scope(scope, table, join_key, value)
6767
end
6868

activerecord/lib/active_record/reflection.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ def check_eager_loadable!
550550
end
551551

552552
def join_id_for(owner) # :nodoc:
553-
Array(join_foreign_key).map { |key| owner[key] }
553+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
554554
end
555555

556556
def through_reflection

activerecord/test/cases/associations_test.rb

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,16 @@
3535
require "models/member"
3636
require "models/membership"
3737
require "models/sharded"
38+
require "models/cpk"
3839
require "models/member_detail"
3940
require "models/organization"
4041

4142

4243
class AssociationsTest < ActiveRecord::TestCase
4344
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
4445
:computers, :people, :readers, :authors, :author_addresses, :author_favorites,
45-
:comments, :posts, :sharded_blogs, :sharded_blog_posts, :sharded_comments, :sharded_tags, :sharded_blog_posts_tags
46+
:comments, :posts, :sharded_blogs, :sharded_blog_posts, :sharded_comments, :sharded_tags, :sharded_blog_posts_tags,
47+
:cpk_orders
4648

4749
def test_eager_loading_should_not_change_count_of_children
4850
liquid = Liquid.create(name: "salty")
@@ -136,6 +138,14 @@ def test_belongs_to_a_model_with_composite_foreign_key_finds_associated_record
136138
assert_equal(blog_post, comment.blog_post)
137139
end
138140

141+
def test_belongs_to_a_cpk_model_by_id_attribute
142+
order = cpk_orders(:cpk_groceries_order_1)
143+
_order_shop_id, order_id = order.id
144+
agreement = Cpk::OrderAgreement.create(order_id: order_id, signature: "signed")
145+
146+
assert_equal(order, agreement.order)
147+
end
148+
139149
def test_belongs_to_a_model_with_composite_primary_key_uses_composite_pk_in_sql
140150
comment = sharded_comments(:great_comment_blog_post_one)
141151

@@ -164,6 +174,14 @@ def test_has_many_association_with_composite_foreign_key_loads_records
164174
assert_includes(comments, sharded_comments(:great_comment_blog_post_one))
165175
end
166176

177+
def test_cpk_model_has_many_records_by_id_attribute
178+
order = cpk_orders(:cpk_groceries_order_1)
179+
_order_shop_id, order_id = order.id
180+
agreements = 2.times.map { Cpk::OrderAgreement.create(order_id: order_id, signature: "signed") }
181+
182+
assert_equal(agreements.sort, order.order_agreements.to_a.sort)
183+
end
184+
167185
def test_has_many_association_from_a_model_with_query_constraints_different_from_the_association
168186
blog_post = sharded_blog_posts(:great_post_blog_one)
169187
blog_post = Sharded::BlogPostWithRevision.find(blog_post.id)

activerecord/test/models/cpk.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33
require_relative "cpk/book"
44
require_relative "cpk/order"
5+
require_relative "cpk/order_agreement"

activerecord/test/models/cpk/order.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ module Cpk
44
class Order < ActiveRecord::Base
55
self.table_name = :cpk_orders
66
self.primary_key = [:shop_id, :id]
7+
8+
has_many :order_agreements, primary_key: :id
79
end
810
end
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
module Cpk
4+
# This is a non composite primary key model that is associated with `Cpk::Order` via `id` only.
5+
class OrderAgreement < ActiveRecord::Base
6+
self.table_name = :cpk_order_agreements
7+
8+
belongs_to :order, primary_key: :id # foreign key is derived as `order_id`
9+
end
10+
end

activerecord/test/schema/schema.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,13 @@
252252
t.string :status
253253
end
254254

255+
create_table :cpk_order_agreements, force: true do |t|
256+
t.integer :order_id
257+
t.string :signature
258+
259+
t.index :order_id
260+
end
261+
255262
create_table :paragraphs, force: true do |t|
256263
t.references :book
257264
end

0 commit comments

Comments
 (0)