aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md7
-rw-r--r--activerecord/lib/active_record/errors.rb7
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb6
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb23
4 files changed, 39 insertions, 4 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index baaa38466b..0841b2f679 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,10 @@
+* Uniqueness validator raises descriptive error when running on a persisted
+ record without primary key.
+
+ Closes #21304.
+
+ *Yves Senn*
+
* Add a native JSON data type support in MySQL.
Example:
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index d589620f8a..718f04871d 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -218,11 +218,12 @@ module ActiveRecord
class UnknownPrimaryKey < ActiveRecordError
attr_reader :model
- def initialize(model)
- super("Unknown primary key for table #{model.table_name} in model #{model}.")
+ def initialize(model, description = nil)
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
+ message += "\n#{description}" if description
+ super(message)
@model = model
end
-
end
# Raised when a relation cannot be mutated because it's already loaded.
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 32d17a1392..5706bbd903 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -18,7 +18,11 @@ module ActiveRecord
relation = build_relation(finder_class, table, attribute, value)
if record.persisted? && finder_class.primary_key.to_s != attribute.to_s
- relation = relation.where.not(finder_class.primary_key => record.id)
+ if finder_class.primary_key
+ relation = relation.where.not(finder_class.primary_key => record.id)
+ else
+ raise UnknownPrimaryKey.new(finder_class, "Can not validate uniqueness for persisted record without primary key.")
+ end
end
relation = scope_relation(record, table, relation)
relation = relation.merge(options[:conditions]) if options[:conditions]
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index ceca2c8366..7502a55391 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -4,6 +4,7 @@ require 'models/reply'
require 'models/warehouse_thing'
require 'models/guid'
require 'models/event'
+require 'models/dashboard'
class Wizard < ActiveRecord::Base
self.abstract_class = true
@@ -446,4 +447,26 @@ class UniquenessValidationTest < ActiveRecord::TestCase
key2.key_number = 10
assert_not key2.valid?
end
+
+ def test_validate_uniqueness_without_primary_key
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "dashboards"
+
+ validates_uniqueness_of :dashboard_id
+
+ def self.name; "Dashboard" end
+ end
+
+ abc = klass.create!(dashboard_id: "abc")
+ assert klass.new(dashboard_id: "xyz").valid?
+ assert_not klass.new(dashboard_id: "abc").valid?
+
+ abc.dashboard_id = "def"
+
+ e = assert_raises ActiveRecord::UnknownPrimaryKey do
+ abc.save!
+ end
+ assert_match(/\AUnknown primary key for table dashboards in model/, e.message)
+ assert_match(/Can not validate uniqueness for persisted record without primary key.\z/, e.message)
+ end
end