aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb17
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb43
3 files changed, 65 insertions, 4 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 639b87d211..11c2e8f773 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -549,9 +549,12 @@ module ActiveRecord
@base.rename_column(name, column_name, new_column_name)
end
- # Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
- # <tt>references</tt> and <tt>belongs_to</tt> are acceptable. The reference column will be an +integer+
- # by default, the <tt>:type</tt> option can be used to specify a different type.
+ # Adds a reference. Optionally adds a +type+ column, if
+ # <tt>:polymorphic</tt> option is provided. <tt>references</tt> and
+ # <tt>belongs_to</tt> are acceptable. The reference column will be an
+ # +integer+ by default, the <tt>:type</tt> option can be used to specify
+ # a different type. A foreign key will be created if a +foreign_key+
+ # option is passed.
#
# t.references(:user)
# t.references(:user, type: "string")
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 0577f868e1..a19bcc86c2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -637,20 +637,31 @@ module ActiveRecord
#
# add_belongs_to(:products, :supplier, polymorphic: true)
#
- # ====== Create a supplier_id, supplier_type columns and appropriate index
+ # ====== Create supplier_id, supplier_type columns and appropriate index
#
# add_reference(:products, :supplier, polymorphic: true, index: true)
#
+ # ====== Create a supplier_id column and appropriate foreign key
+ #
+ # add_reference(:products, :supplier, foreign_key: true)
+ #
def add_reference(
table_name,
ref_name,
polymorphic: false,
index: false,
+ foreign_key: false,
type: :integer,
**options
)
polymorphic_options = polymorphic.is_a?(Hash) ? polymorphic : options
index_options = index.is_a?(Hash) ? index : {}
+ foreign_key_options = foreign_key.is_a?(Hash) ? foreign_key : {}
+
+ if polymorphic && foreign_key
+ raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
+ end
+
add_column(table_name, "#{ref_name}_id", type, options)
if polymorphic
@@ -660,6 +671,10 @@ module ActiveRecord
if index
add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options)
end
+
+ if foreign_key
+ add_foreign_key(table_name, ref_name.to_s.pluralize, foreign_key_options)
+ end
end
alias :add_belongs_to :add_reference
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 2fa7584a60..c6f0d50a8d 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -54,6 +54,49 @@ module ActiveRecord
end
end
end
+
+ test "foreign keys can be created while changing the table" do
+ @connection.create_table :testings
+ @connection.change_table :testings do |t|
+ t.references :testing_parent, foreign_key: true
+ end
+
+ fk = @connection.foreign_keys("testings").first
+ assert_equal "testings", fk.from_table
+ assert_equal "testing_parents", fk.to_table
+ end
+
+ test "foreign keys are not added by default when changing the table" do
+ @connection.create_table :testings
+ @connection.change_table :testings do |t|
+ t.references :testing_parent
+ end
+
+ assert_equal [], @connection.foreign_keys("testings")
+ end
+
+ test "foreign keys accept options when changing the table" do
+ @connection.change_table :testing_parents do |t|
+ t.integer :other_id
+ t.index :other_id, unique: true
+ end
+ @connection.create_table :testings
+ @connection.change_table :testings do |t|
+ t.references :testing_parent, foreign_key: { primary_key: :other_id }
+ end
+
+ fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" }
+ assert_equal "other_id", fk.primary_key
+ end
+
+ test "foreign keys cannot be added to polymorphic relations when changing the table" do
+ @connection.create_table :testings
+ @connection.change_table :testings do |t|
+ assert_raises(ArgumentError) do
+ t.references :testing_parent, polymorphic: true, foreign_key: true
+ end
+ end
+ end
end
end
end