From 006eb25ccdf200d305bc5af46831548ffd6eec2c Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 10 Feb 2021 11:29:13 -0500 Subject: [PATCH] Raise error on invalid fixture primary key When you're using a custom primary key on a belongs_to and you're trying to load that value through the association shorthand in a fixture, you end up getting the primary key of the table and not the primary key specified in the join. This makes sense to keep as the behavior because it's super fast (just hashing the name of the fixture), but it's still surprising so we should want the developer that it's not possible to do what they want. --- .../active_record/fixture_set/table_row.rb | 33 +++++++++++++++++-- activerecord/test/cases/fixtures_test.rb | 12 +++++++ .../primary_key_error/primary_key_error.yml | 6 ++++ 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 activerecord/test/fixtures/primary_key_error/primary_key_error.yml diff --git a/activerecord/lib/active_record/fixture_set/table_row.rb b/activerecord/lib/active_record/fixture_set/table_row.rb index 6b4c7a935f..c96c0fed62 100644 --- a/activerecord/lib/active_record/fixture_set/table_row.rb +++ b/activerecord/lib/active_record/fixture_set/table_row.rb @@ -39,6 +39,29 @@ module ActiveRecord end end + class PrimaryKeyError < StandardError # :nodoc: + def initialize(label, association, value) + super(<<~MSG) + Unable to set #{association.name} to #{value} because the association has a + custom primary key (#{association.join_primary_key}) that does not match the + associated table's primary key (#{association.klass.primary_key}). + + To fix this, change your fixture from + + #{label}: + #{association.name}: #{value} + + to + + #{label}: + #{association.foreign_key}: **value** + + where **value** is the #{association.join_primary_key} value for the + associated #{association.klass.name} record. + MSG + end + end + def initialize(fixture, table_rows:, label:, now:) @table_rows = table_rows @label = label @@ -119,9 +142,13 @@ module ActiveRecord fk_name = association.join_foreign_key if association.name.to_s != fk_name && value = @row.delete(association.name.to_s) - if association.polymorphic? && value.sub!(/\s*\(([^)]*)\)\s*$/, "") - # support polymorphic belongs_to as "label (Type)" - @row[association.join_foreign_type] = $1 + if association.polymorphic? + if value.sub!(/\s*\(([^)]*)\)\s*$/, "") + # support polymorphic belongs_to as "label (Type)" + @row[association.join_foreign_type] = $1 + end + elsif association.join_primary_key != association.klass.primary_key + raise PrimaryKeyError.new(@label, association, value) end fk_type = reflection_class.type_for_attribute(fk_name).type diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index a466cccac9..dfd90c14ab 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -6,6 +6,7 @@ require "models/admin" require "models/admin/account" require "models/admin/randomly_named_c1" require "models/admin/user" +require "models/author" require "models/binary" require "models/book" require "models/bulb" @@ -18,6 +19,7 @@ require "models/course" require "models/developer" require "models/dog" require "models/doubloon" +require "models/essay" require "models/joke" require "models/matey" require "models/other_dog" @@ -1492,6 +1494,16 @@ class FileFixtureConflictTest < ActiveRecord::TestCase end end +class PrimaryKeyErrorTest < ActiveRecord::TestCase + test "generates the correct value" do + e = assert_raise(ActiveRecord::FixtureSet::TableRow::PrimaryKeyError) do + ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/primary_key_error", "primary_key_error") + end + + assert_includes e.message, "Unable to set" + end +end + if current_adapter?(:SQLite3Adapter) && !in_memory_db? class MultipleFixtureConnectionsTest < ActiveRecord::TestCase include ActiveRecord::TestFixtures diff --git a/activerecord/test/fixtures/primary_key_error/primary_key_error.yml b/activerecord/test/fixtures/primary_key_error/primary_key_error.yml new file mode 100644 index 0000000000..b5edcec4ac --- /dev/null +++ b/activerecord/test/fixtures/primary_key_error/primary_key_error.yml @@ -0,0 +1,6 @@ +_fixture: + model_class: Author + +david: + name: David + owned_essay: a_modest_proposal