Skip to content

Commit 7d0f4e0

Browse files
authored
Merge pull request #1857 from bf4/smarter_association_id_lookup
Smarter association id lookup-- no db hit on belongs_to for id-only
2 parents 0f59d64 + 6e41528 commit 7d0f4e0

File tree

6 files changed

+74
-7
lines changed

6 files changed

+74
-7
lines changed

lib/active_model/serializer/association.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ def meta
3838
reflection.options[:meta]
3939
end
4040

41+
def belongs_to?
42+
reflection.foreign_key_on == :self
43+
end
44+
4145
def polymorphic?
4246
true == reflection_options[:polymorphic]
4347
end

lib/active_model/serializer/belongs_to_reflection.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ module ActiveModel
22
class Serializer
33
# @api private
44
class BelongsToReflection < Reflection
5+
# @api private
6+
def foreign_key_on
7+
:self
8+
end
59
end
610
end
711
end

lib/active_model/serializer/reflection.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,23 @@ class Serializer
4747
#
4848
# So you can inspect reflections in your Adapters.
4949
class Reflection < Field
50+
attr_reader :foreign_key, :type
51+
5052
def initialize(*)
5153
super
5254
options[:links] = {}
5355
options[:include_data_setting] = Serializer.config.include_data_default
5456
options[:meta] = nil
57+
@type = options.fetch(:type) do
58+
class_name = options.fetch(:class_name, name.to_s.camelize.singularize)
59+
class_name.underscore.pluralize.to_sym
60+
end
61+
@foreign_key =
62+
if collection?
63+
"#{name.to_s.singularize}_ids".to_sym
64+
else
65+
"#{name}_id".to_sym
66+
end
5567
end
5668

5769
# @api public
@@ -150,6 +162,11 @@ def value(serializer, include_slice)
150162
end
151163
end
152164

165+
# @api private
166+
def foreign_key_on
167+
:related
168+
end
169+
153170
# Build association. This method is used internally to
154171
# build serializer's association by its reflection.
155172
#

lib/active_model_serializers/adapter/json_api/relationship.rb

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,21 @@ def data_for(association)
4343
end
4444

4545
def data_for_one(association)
46-
# TODO(BF): Process relationship without evaluating lazy_association
47-
serializer = association.lazy_association.serializer
48-
if (virtual_value = association.virtual_value)
49-
virtual_value
50-
elsif serializer && association.object
51-
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
46+
if association.belongs_to? &&
47+
parent_serializer.object.respond_to?(association.reflection.foreign_key)
48+
id = parent_serializer.object.send(association.reflection.foreign_key)
49+
type = association.reflection.type.to_s
50+
ResourceIdentifier.for_type_with_id(type, id, serializable_resource_options)
5251
else
53-
nil
52+
# TODO(BF): Process relationship without evaluating lazy_association
53+
serializer = association.lazy_association.serializer
54+
if (virtual_value = association.virtual_value)
55+
virtual_value
56+
elsif serializer && association.object
57+
ResourceIdentifier.new(serializer, serializable_resource_options).as_json
58+
else
59+
nil
60+
end
5461
end
5562
end
5663

lib/active_model_serializers/adapter/json_api/resource_identifier.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ def self.type_for(class_name, serializer_type = nil, transform_options = {})
2222
JsonApi.send(:transform_key_casing!, raw_type, transform_options)
2323
end
2424

25+
def self.for_type_with_id(type, id, options)
26+
{
27+
id: id.to_s,
28+
type: type_for(:no_class_needed, type, options)
29+
}
30+
end
31+
2532
# {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects}
2633
def initialize(serializer, options)
2734
@id = id_for(serializer)

test/serializers/associations_test.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,34 @@ def test_associations_custom_keys
137137
assert expected_association_keys.include? :site
138138
end
139139

140+
class BelongsToBlogModel < ::Model
141+
attributes :id, :title
142+
associations :blog
143+
end
144+
class BelongsToBlogModelSerializer < ActiveModel::Serializer
145+
type :posts
146+
belongs_to :blog
147+
end
148+
149+
def test_belongs_to_doesnt_load_record
150+
attributes = { id: 1, title: 'Belongs to Blog', blog: Blog.new(id: 5) }
151+
post = BelongsToBlogModel.new(attributes)
152+
class << post
153+
def blog
154+
fail 'should use blog_id'
155+
end
156+
157+
def blog_id
158+
5
159+
end
160+
end
161+
162+
actual = serializable(post, adapter: :json_api, serializer: BelongsToBlogModelSerializer).as_json
163+
expected = { data: { id: '1', type: 'posts', relationships: { blog: { data: { id: '5', type: 'blogs' } } } } }
164+
165+
assert_equal expected, actual
166+
end
167+
140168
class InlineAssociationTestPostSerializer < ActiveModel::Serializer
141169
has_many :comments
142170
has_many :comments, key: :last_comments do

0 commit comments

Comments
 (0)