From 273b7e7f3031a5867e473a4a1e079351ae90fcbf Mon Sep 17 00:00:00 2001 From: Manuel Thomassen Date: Wed, 25 May 2016 09:27:37 +0200 Subject: [PATCH 1/2] belongs_to causes unnecessary db hit --- test/serializers/associations_test.rb | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test/serializers/associations_test.rb b/test/serializers/associations_test.rb index f6603a8d..c1b164b8 100644 --- a/test/serializers/associations_test.rb +++ b/test/serializers/associations_test.rb @@ -137,6 +137,34 @@ module ActiveModel assert expected_association_keys.include? :site end + class BelongsToBlogModel < ::Model + attributes :id, :title + associations :blog + end + class BelongsToBlogModelSerializer < ActiveModel::Serializer + type :posts + belongs_to :blog + end + + def test_belongs_to_doesnt_load_record + attributes = { id: 1, title: 'Belongs to Blog', blog: Blog.new(id: 5) } + post = BelongsToBlogModel.new(attributes) + class << post + def blog + fail 'should use blog_id' + end + + def blog_id + 5 + end + end + + actual = serializable(post, adapter: :json_api, serializer: BelongsToBlogModelSerializer).as_json + expected = { data: { id: '1', type: 'posts', relationships: { blog: { data: { id: '5', type: 'blogs' } } } } } + + assert_equal expected, actual + end + class InlineAssociationTestPostSerializer < ActiveModel::Serializer has_many :comments has_many :comments, key: :last_comments do From 6e4152851554b33d8cdf607fe4e175dd1aafd1c7 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Sun, 30 Apr 2017 18:31:35 -0500 Subject: [PATCH 2/2] Skip eval relationships object on belongs to --- lib/active_model/serializer/association.rb | 4 ++++ .../serializer/belongs_to_reflection.rb | 4 ++++ lib/active_model/serializer/reflection.rb | 17 +++++++++++++++ .../adapter/json_api/relationship.rb | 21 ++++++++++++------- .../adapter/json_api/resource_identifier.rb | 7 +++++++ 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/lib/active_model/serializer/association.rb b/lib/active_model/serializer/association.rb index 767fd8a2..7ce82316 100644 --- a/lib/active_model/serializer/association.rb +++ b/lib/active_model/serializer/association.rb @@ -38,6 +38,10 @@ module ActiveModel reflection.options[:meta] end + def belongs_to? + reflection.foreign_key_on == :self + end + def polymorphic? true == reflection_options[:polymorphic] end diff --git a/lib/active_model/serializer/belongs_to_reflection.rb b/lib/active_model/serializer/belongs_to_reflection.rb index 67dbe79a..04bbc6fc 100644 --- a/lib/active_model/serializer/belongs_to_reflection.rb +++ b/lib/active_model/serializer/belongs_to_reflection.rb @@ -2,6 +2,10 @@ module ActiveModel class Serializer # @api private class BelongsToReflection < Reflection + # @api private + def foreign_key_on + :self + end end end end diff --git a/lib/active_model/serializer/reflection.rb b/lib/active_model/serializer/reflection.rb index 49eb7600..d3daab4b 100644 --- a/lib/active_model/serializer/reflection.rb +++ b/lib/active_model/serializer/reflection.rb @@ -47,11 +47,23 @@ module ActiveModel # # So you can inspect reflections in your Adapters. class Reflection < Field + attr_reader :foreign_key, :type + def initialize(*) super options[:links] = {} options[:include_data_setting] = Serializer.config.include_data_default options[:meta] = nil + @type = options.fetch(:type) do + class_name = options.fetch(:class_name, name.to_s.camelize.singularize) + class_name.underscore.pluralize.to_sym + end + @foreign_key = + if collection? + "#{name.to_s.singularize}_ids".to_sym + else + "#{name}_id".to_sym + end end # @api public @@ -150,6 +162,11 @@ module ActiveModel end end + # @api private + def foreign_key_on + :related + end + # Build association. This method is used internally to # build serializer's association by its reflection. # diff --git a/lib/active_model_serializers/adapter/json_api/relationship.rb b/lib/active_model_serializers/adapter/json_api/relationship.rb index fc2f116f..5d7399a3 100644 --- a/lib/active_model_serializers/adapter/json_api/relationship.rb +++ b/lib/active_model_serializers/adapter/json_api/relationship.rb @@ -43,14 +43,21 @@ module ActiveModelSerializers end def data_for_one(association) - # TODO(BF): Process relationship without evaluating lazy_association - serializer = association.lazy_association.serializer - if (virtual_value = association.virtual_value) - virtual_value - elsif serializer && association.object - ResourceIdentifier.new(serializer, serializable_resource_options).as_json + if association.belongs_to? && + parent_serializer.object.respond_to?(association.reflection.foreign_key) + id = parent_serializer.object.send(association.reflection.foreign_key) + type = association.reflection.type.to_s + ResourceIdentifier.for_type_with_id(type, id, serializable_resource_options) else - nil + # TODO(BF): Process relationship without evaluating lazy_association + serializer = association.lazy_association.serializer + if (virtual_value = association.virtual_value) + virtual_value + elsif serializer && association.object + ResourceIdentifier.new(serializer, serializable_resource_options).as_json + else + nil + end end end diff --git a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb index af6f5f9e..8931f899 100644 --- a/lib/active_model_serializers/adapter/json_api/resource_identifier.rb +++ b/lib/active_model_serializers/adapter/json_api/resource_identifier.rb @@ -22,6 +22,13 @@ module ActiveModelSerializers JsonApi.send(:transform_key_casing!, raw_type, transform_options) end + def self.for_type_with_id(type, id, options) + { + id: id.to_s, + type: type_for(:no_class_needed, type, options) + } + end + # {http://jsonapi.org/format/#document-resource-identifier-objects Resource Identifier Objects} def initialize(serializer, options) @id = id_for(serializer)