aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYves Senn <yves.senn@gmail.com>2015-02-16 09:08:38 +0100
committerYves Senn <yves.senn@gmail.com>2015-02-16 09:39:59 +0100
commit6bd777c85156aca0cf2d1f34d8c1fe37d96ac3d9 (patch)
treee12a4abb9b323d80de9d5af7556dad301d3a18d8
parent8980da84bc33b0e2b36c92362d74280658441581 (diff)
parentf0ae503d1931a2f269db3437550be3173ba06bce (diff)
downloadrails-6bd777c85156aca0cf2d1f34d8c1fe37d96ac3d9.tar.gz
rails-6bd777c85156aca0cf2d1f34d8c1fe37d96ac3d9.tar.bz2
rails-6bd777c85156aca0cf2d1f34d8c1fe37d96ac3d9.zip
Merge pull request #18662 from estum/foreign-key-exists
Add `foreign_key_exists?` method.
-rw-r--r--activerecord/CHANGELOG.md4
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb41
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb21
4 files changed, 63 insertions, 15 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index e1cace7d88..ff8dd6b72d 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Add `foreign_key_exists?` method.
+
+ *Tõnis Simo*
+
* Use SQL COUNT and LIMIT 1 queries for `none?` and `one?` methods if no block or limit is given,
instead of loading the entire collection to memory.
This applies to relations (e.g. `User.all`) as well as associations (e.g. `account.users`)
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 8cd4b8e5b2..a2777fcd0a 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -50,6 +50,14 @@ module ActiveRecord
options[:primary_key] != default_primary_key
end
+ def defined_for?(options_or_to_table = {})
+ if options_or_to_table.is_a?(Hash)
+ options_or_to_table.all? {|key, value| options[key].to_s == value.to_s }
+ else
+ to_table == options_or_to_table.to_s
+ end
+ end
+
private
def default_primary_key
"id"
@@ -651,6 +659,10 @@ module ActiveRecord
@base.add_foreign_key(name, *args)
end
+ def foreign_key_exists?(*args) # :nodoc:
+ @base.foreign_key_exists?(name, *args)
+ end
+
private
def native
@base.native_database_types
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 8217468024..0438c95bd7 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -757,21 +757,7 @@ module ActiveRecord
def remove_foreign_key(from_table, options_or_to_table = {})
return unless supports_foreign_keys?
- if options_or_to_table.is_a?(Hash)
- options = options_or_to_table
- else
- options = { column: foreign_key_column_for(options_or_to_table) }
- end
-
- fk_name_to_delete = options.fetch(:name) do
- fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s }
-
- if fk_to_delete
- fk_to_delete.name
- else
- raise ArgumentError, "Table '#{from_table}' has no foreign key on column '#{options[:column]}'"
- end
- end
+ fk_name_to_delete = foreign_key_for!(from_table, options_or_to_table).name
at = create_alter_table from_table
at.drop_foreign_key fk_name_to_delete
@@ -779,6 +765,31 @@ module ActiveRecord
execute schema_creation.accept(at)
end
+ # Checks to see if a foreign key exists on a table for a given foreign key definition.
+ #
+ # # Check a foreign key exists
+ # foreign_key_exists?(:accounts, :branches)
+ #
+ # # Check a foreign key on specified column exists
+ # foreign_key_exists?(:accounts, column: :owner_id)
+ #
+ # # Check a foreign key with a custom name exists
+ # foreign_key_exists?(:accounts, name: "special_fk_name")
+ #
+ def foreign_key_exists?(from_table, options_or_to_table = {})
+ foreign_key_for(from_table, options_or_to_table).present?
+ end
+
+ def foreign_key_for(from_table, options_or_to_table = {}) # :nodoc:
+ return unless supports_foreign_keys?
+ foreign_keys(from_table).detect {|fk| fk.defined_for? options_or_to_table }
+ end
+
+ def foreign_key_for!(from_table, options_or_to_table = {}) # :nodoc:
+ foreign_key_for(from_table, options_or_to_table) or \
+ raise ArgumentError, "Table '#{from_table}' has no foreign key for #{options_or_to_table}"
+ end
+
def foreign_key_column_for(table_name) # :nodoc:
"#{table_name.to_s.singularize}_id"
end
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index b2f2d077eb..7f4790bf3e 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -147,6 +147,27 @@ module ActiveRecord
assert_equal :nullify, fk.on_update
end
+ def test_foreign_key_exists
+ @connection.add_foreign_key :astronauts, :rockets
+
+ assert @connection.foreign_key_exists?(:astronauts, :rockets)
+ assert_not @connection.foreign_key_exists?(:astronauts, :stars)
+ end
+
+ def test_foreign_key_exists_by_column
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
+
+ assert @connection.foreign_key_exists?(:astronauts, column: "rocket_id")
+ assert_not @connection.foreign_key_exists?(:astronauts, column: "star_id")
+ end
+
+ def test_foreign_key_exists_by_name
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
+
+ assert @connection.foreign_key_exists?(:astronauts, name: "fancy_named_fk")
+ assert_not @connection.foreign_key_exists?(:astronauts, name: "other_fancy_named_fk")
+ end
+
def test_remove_foreign_key_inferes_column
@connection.add_foreign_key :astronauts, :rockets