提交 2e9e647f 编写于 作者: J Jon Leighton

Raise an exception on unknown primary key inside AssociationReflection.

An association between two models cannot be made if a relevant key is
unknown, so fail fast rather than generating invalid SQL. Fixes #3207.
上级 64747654
...@@ -169,4 +169,17 @@ def initialize(errors) ...@@ -169,4 +169,17 @@ def initialize(errors)
@errors = errors @errors = errors
end end
end end
# Raised when a primary key is needed, but there is not one specified in the schema or model.
class UnknownPrimaryKey < ActiveRecordError
attr_reader :model
def initialize(model)
@model = model
end
def message
"Unknown primary key for table #{model.table_name} in model #{model}."
end
end
end end
...@@ -213,11 +213,11 @@ def association_foreign_key ...@@ -213,11 +213,11 @@ def association_foreign_key
# klass option is necessary to support loading polymorphic associations # klass option is necessary to support loading polymorphic associations
def association_primary_key(klass = nil) def association_primary_key(klass = nil)
options[:primary_key] || (klass || self.klass).primary_key options[:primary_key] || primary_key(klass || self.klass)
end end
def active_record_primary_key def active_record_primary_key
@active_record_primary_key ||= options[:primary_key] || active_record.primary_key @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
end end
def counter_cache_column def counter_cache_column
...@@ -357,6 +357,10 @@ def derive_foreign_key ...@@ -357,6 +357,10 @@ def derive_foreign_key
active_record.name.foreign_key active_record.name.foreign_key
end end
end end
def primary_key(klass)
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
end
end end
# Holds all the meta-data about a :through association as it was specified # Holds all the meta-data about a :through association as it was specified
...@@ -461,7 +465,7 @@ def nested? ...@@ -461,7 +465,7 @@ def nested?
# We want to use the klass from this reflection, rather than just delegate straight to # We want to use the klass from this reflection, rather than just delegate straight to
# the source_reflection, because the source_reflection may be polymorphic. We still # the source_reflection, because the source_reflection may be polymorphic. We still
# need to respect the source_reflection's :primary_key option, though. # need to respect the source_reflection's :primary_key option, though.
def association_primary_key(klass = self.klass) def association_primary_key(klass = nil)
# Get the "actual" source reflection if the immediate source reflection has a # Get the "actual" source reflection if the immediate source reflection has a
# source reflection itself # source reflection itself
source_reflection = self.source_reflection source_reflection = self.source_reflection
...@@ -469,7 +473,7 @@ def association_primary_key(klass = self.klass) ...@@ -469,7 +473,7 @@ def association_primary_key(klass = self.klass)
source_reflection = source_reflection.source_reflection source_reflection = source_reflection.source_reflection
end end
source_reflection.options[:primary_key] || klass.primary_key source_reflection.options[:primary_key] || primary_key(klass || self.klass)
end end
# Gets an array of possible <tt>:through</tt> source reflection names: # Gets an array of possible <tt>:through</tt> source reflection names:
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
require 'models/subscription' require 'models/subscription'
require 'models/tag' require 'models/tag'
require 'models/sponsor' require 'models/sponsor'
require 'models/edge'
class ReflectionTest < ActiveRecord::TestCase class ReflectionTest < ActiveRecord::TestCase
include ActiveRecord::Reflection include ActiveRecord::Reflection
...@@ -252,11 +253,25 @@ def test_association_primary_key ...@@ -252,11 +253,25 @@ def test_association_primary_key
assert_equal "custom_primary_key", Author.reflect_on_association(:tags_with_primary_key).association_primary_key.to_s # nested assert_equal "custom_primary_key", Author.reflect_on_association(:tags_with_primary_key).association_primary_key.to_s # nested
end end
def test_association_primary_key_raises_when_missing_primary_key
reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :edge, {}, Author)
assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.association_primary_key }
through = ActiveRecord::Reflection::ThroughReflection.new(:fuu, :edge, {}, Author)
through.stubs(:source_reflection).returns(stub_everything(:options => {}, :class_name => 'Edge'))
assert_raises(ActiveRecord::UnknownPrimaryKey) { through.association_primary_key }
end
def test_active_record_primary_key def test_active_record_primary_key
assert_equal "nick", Subscriber.reflect_on_association(:subscriptions).active_record_primary_key.to_s assert_equal "nick", Subscriber.reflect_on_association(:subscriptions).active_record_primary_key.to_s
assert_equal "name", Author.reflect_on_association(:essay).active_record_primary_key.to_s assert_equal "name", Author.reflect_on_association(:essay).active_record_primary_key.to_s
end end
def test_active_record_primary_key_raises_when_missing_primary_key
reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :author, {}, Edge)
assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.active_record_primary_key }
end
def test_foreign_type def test_foreign_type
assert_equal "sponsorable_type", Sponsor.reflect_on_association(:sponsorable).foreign_type.to_s assert_equal "sponsorable_type", Sponsor.reflect_on_association(:sponsorable).foreign_type.to_s
assert_equal "sponsorable_type", Sponsor.reflect_on_association(:thing).foreign_type.to_s assert_equal "sponsorable_type", Sponsor.reflect_on_association(:thing).foreign_type.to_s
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册