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
```
This commit is contained in:
Nikita Vasilevsky 2023-04-12 16:01:40 +00:00
parent e11ebc04cf
commit 5cbdfba670
7 changed files with 41 additions and 3 deletions

View File

@ -62,7 +62,7 @@ module ActiveRecord
table = reflection.aliased_table
primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
primary_key_foreign_key_pairs.each do |join_key, foreign_key|
value = transform_value(owner[foreign_key])
value = transform_value(owner._read_attribute(foreign_key))
scope = apply_scope(scope, table, join_key, value)
end

View File

@ -550,7 +550,7 @@ module ActiveRecord
end
def join_id_for(owner) # :nodoc:
Array(join_foreign_key).map { |key| owner[key] }
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
end
def through_reflection

View File

@ -35,6 +35,7 @@ require "models/essay"
require "models/member"
require "models/membership"
require "models/sharded"
require "models/cpk"
require "models/member_detail"
require "models/organization"
@ -42,7 +43,8 @@ require "models/organization"
class AssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
:computers, :people, :readers, :authors, :author_addresses, :author_favorites,
:comments, :posts, :sharded_blogs, :sharded_blog_posts, :sharded_comments, :sharded_tags, :sharded_blog_posts_tags
:comments, :posts, :sharded_blogs, :sharded_blog_posts, :sharded_comments, :sharded_tags, :sharded_blog_posts_tags,
:cpk_orders
def test_eager_loading_should_not_change_count_of_children
liquid = Liquid.create(name: "salty")
@ -136,6 +138,14 @@ class AssociationsTest < ActiveRecord::TestCase
assert_equal(blog_post, comment.blog_post)
end
def test_belongs_to_a_cpk_model_by_id_attribute
order = cpk_orders(:cpk_groceries_order_1)
_order_shop_id, order_id = order.id
agreement = Cpk::OrderAgreement.create(order_id: order_id, signature: "signed")
assert_equal(order, agreement.order)
end
def test_belongs_to_a_model_with_composite_primary_key_uses_composite_pk_in_sql
comment = sharded_comments(:great_comment_blog_post_one)
@ -164,6 +174,14 @@ class AssociationsTest < ActiveRecord::TestCase
assert_includes(comments, sharded_comments(:great_comment_blog_post_one))
end
def test_cpk_model_has_many_records_by_id_attribute
order = cpk_orders(:cpk_groceries_order_1)
_order_shop_id, order_id = order.id
agreements = 2.times.map { Cpk::OrderAgreement.create(order_id: order_id, signature: "signed") }
assert_equal(agreements.sort, order.order_agreements.to_a.sort)
end
def test_has_many_association_from_a_model_with_query_constraints_different_from_the_association
blog_post = sharded_blog_posts(:great_post_blog_one)
blog_post = Sharded::BlogPostWithRevision.find(blog_post.id)

View File

@ -2,3 +2,4 @@
require_relative "cpk/book"
require_relative "cpk/order"
require_relative "cpk/order_agreement"

View File

@ -4,5 +4,7 @@ module Cpk
class Order < ActiveRecord::Base
self.table_name = :cpk_orders
self.primary_key = [:shop_id, :id]
has_many :order_agreements, primary_key: :id
end
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
module Cpk
# This is a non composite primary key model that is associated with `Cpk::Order` via `id` only.
class OrderAgreement < ActiveRecord::Base
self.table_name = :cpk_order_agreements
belongs_to :order, primary_key: :id # foreign key is derived as `order_id`
end
end

View File

@ -252,6 +252,13 @@ ActiveRecord::Schema.define do
t.string :status
end
create_table :cpk_order_agreements, force: true do |t|
t.integer :order_id
t.string :signature
t.index :order_id
end
create_table :paragraphs, force: true do |t|
t.references :book
end