How do I drop a foreign key if it exists in Ruby on Rails?
Asked Answered
K

6

16

There's a function called index_exists? in ActionRecord, but no foreign_key_exists? on Rails 4.2.7.

So when I call remove_foreign_key :parties, :franchise_groups on some databases it breaks.

What should I use?


Update

My code

class RemoveForeignKey < ActiveRecord::Migration
  def up
    if foreign_key_exists?(:parties, :franchise_groups)
      remove_foreign_key :parties, :franchise_groups
    end
  end
end

gives the error

== 20161107163800 RemoveForeignKey: migrating =================================
-- foreign_key_exists?(:parties, :franchise_groups)
rake aborted!
An error has occurred, all later migrations canceled:

undefined method `foreign_key_exists?' for #<RemoveForeignKey:0x00000007ea0b58>
/home/rje/.rvm/gems/ruby-2.3.0/gems/activerecord-4.2.7/lib/active_record/migration.rb:664:in `block in method_missing'
Know answered 7/11, 2016 at 14:41 Comment(0)
C
21

but no foreign_key_exists?

There is foreign_key_exists? :)

Checks to see if a foreign key exists on a table for a given foreign key definition.

# Checks to see if a foreign key exists.
  foreign_key_exists?(:accounts, :branches)

# Checks to see if a foreign key on a specified column exists. foreign_key_exists?(:accounts, column: :owner_id)

# Checks to see if a foreign key with a custom name exists. foreign_key_exists?(:accounts, name: "special_fk_name")

Alternatively, you can use foreign_keys:

if foreign_keys(:table_name).include?(foreign_key_name)
  # do stuff
end
Cryptology answered 7/11, 2016 at 14:44 Comment(5)
Thanks. I've given the full error in my question now. Perhaps I'm not understanding the docs.Know
can you check inside up method if self.class.included_modules.include?(ActiveRecord::ConnectionAdapters::SchemaStatements)?Cryptology
puts self.class.included_modules.include?(ActiveRecord::Connectio‌​nAdapters::SchemaSta‌​tements) just gives me the error uninitialized constant Connectio‌​nAdapters. Perhaps this doesn't exist on Rails 4.2?Know
@Know you can use if foreign_keys(:accounts).include?(foreign_key_name)Cryptology
Available in rails 5.0+ onlyTessitura
S
7

On Rails 4 there is no foreign_key_exists so I came up with the following solution:

remove_foreign_key :events, column: :subscribers_selector_id if foreign_keys(:events).map(&:column).include?("subscribers_selector_id")
Superstition answered 21/3, 2018 at 8:56 Comment(0)
M
3

Rails 7+ if_exists / if_not_exists options

Rails 7 adds if_exists option to remove_foreign_key in order to not raise an error when the foreign key is already removed.

Rails 7 adds if_not_exists option to add_foreign_key in order to not raise an error when the foreign key is already added.

As a result, a migration can be written in the following way:

class RemoveFranchiseGroupForeignKeysFromParties < ActiveRecord::Migration
  def up
    remove_foreign_key :parties, :franchise_groups, if_exists: true
  end

  def down
    add_foreign_key :parties, :franchise_groups, if_not_exists: true
  end
end

Sources:

Muzzleloader answered 7/7, 2021 at 16:6 Comment(0)
L
2

I think you can use something like this

def up
  remove_foreign_key :parties, column: :franchise_groups
end

def down
  add_foreign_key :parties, :franchise_groups
end
Lanate answered 7/11, 2016 at 14:55 Comment(1)
Thank you. But the problem is checking if the key is there first, not deleting it.Know
S
2

It works on connection:

ActiveRecord::Base.connection.foreign_key_exists?(:parties, :franchise_groups) 
Stagecraft answered 2/2, 2017 at 15:4 Comment(0)
A
2

My Rails version doesn't seem to have "foreign_key_exists?" (Rails 4.2.6), so I'm using Array#any? to search through the results from "foreign_keys" and determine whether a given foreign key exists:

foreign_keys("parties").any?{|k| k[:to_table] == "franchise_groups"}

You can use it as such:

if foreign_keys("parties").any?{|k| k[:to_table] == "franchise_groups"}
remove_foreign_key :parties, column: :franchise_group_id
end
Amati answered 2/2, 2018 at 20:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.