Search code examples
ruby-on-railspostgresqlruby-on-rails-5rails-migrationspostgresql-9.6

Why are my add_foreign_key on_delete: :cascade designations not being transferred to the schema


In an effort to properly handle Postgres's ForeignKeyViolation warnings, I've gone through each of my models and added on_delete: commands as shown below.

The format I'm following is

add_foreign_key <:named_table>, <:related_table>, on_delete: :cascade

However, after making these changes and running rails db:reset I notice these extra parameters are not being passed into the resulting schema.rb file and I still receive the abovementioned error when trying to delete an image.

Is there something I'm doing wrong either in my procedure or my syntax? Thank you in advance!

12345_create_document_images.rb

class CreateDocumentImages < ActiveRecord::Migration[5.1]
  def change
    create_table :document_images do |t|
      t.references :document, foreign_key: true
      t.references :image, foreign_key: true

      t.timestamps
    end

    add_foreign_key :document_images, :documents, on_delete: :cascade
    add_foreign_key :document_images, :images, on_delete: :cascade
  end
end

schema.rb

 add_foreign_key "document_images", "images"
 add_foreign_key "document_images", "documents"

Solution

  • When you say this:

    t.references :document, foreign_key: true
    

    The foreign_key: true option creates the same foreign key that:

    add_foreign_key :document_images, :documents, on_delete: :cascade
    

    does. When the add_foreign_key method executes, the FK already exists so it should trigger an PG::DuplicateObject exception at least it does for me. I'm not sure why you're not getting an exception but that doesn't matter, what does matter is that the FK from create_table will be there before you try to add an FK with on_delete: :cascade via your add_foreign_key calls.

    The solution is to let t.references create the FK with on_delete: :cascade and leave out the explicit add_foreign_key calls:

    class CreateDocumentImages < ActiveRecord::Migration[5.1]
      def change
        create_table :document_images do |t|
          t.references :document, foreign_key: { on_delete: :cascade }
          t.references :image, foreign_key: { on_delete: :cascade}
          t.timestamps
        end
      end
    end
    

    I also renamed the migration to match what it is really doing.

    Added by Spectator6: Something else that @mu_is_too_short introduced me to is the use of rails db:migrate:redo which led me to also learn about rails db:migrate:reset. Previously, I had only been using rails db:reset, but the moment I implemented his suggestions and then ran rails db:migration:reset followed by rails db:reset, everything clicked! The schema reflected the new database triggers on the foreign_key and trying out the functionality in development worked as expected. Very cool! All props to @mu_is_too_short!