Skip to content

Commit c7481e3

Browse files
committed
Handle failing foreign key additions
We've added some foreign key constraints recently, and have since come up with a nicer way of guiding users towards fixing their data intentionally. This change gives them a nice error message and allows them to change the migration to remove offending records. Co-Authored-By: thomas@vondeyen.com
1 parent db99cba commit c7481e3

File tree

2 files changed

+87
-4
lines changed

2 files changed

+87
-4
lines changed
Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,28 @@
11
# frozen_string_literal: true
22

33
class AddAddressbookForeignKey < ActiveRecord::Migration[7.0]
4-
def change
4+
def up
5+
# Uncomment the following code to remove orphaned records if this migration fails
6+
#
7+
# say_with_time "Removing orphaned address book entries (no corresponding address)" do
8+
# Spree::UserAddress.left_joins(:address).where(spree_addresses: { id: nil }).delete_all
9+
# end
10+
511
add_foreign_key :spree_user_addresses, :spree_addresses, column: :address_id, null: false
12+
rescue ActiveRecord::StatementInvalid => e
13+
if e.cause.is_a?(PG::ForeignKeyViolation) || e.cause.is_a?(Mysql2::Error) || e.cause.is_a?(SQLite3::ConstraintException)
14+
Rails.logger.warn <<~MSG
15+
⚠️ Foreign key constraint failed when adding :spree_user_addresses => :spree_addresses.
16+
To fix this:
17+
1. Uncomment the code that removes orphaned records.
18+
2. Rerun the migration.
19+
Offending error: #{e.cause.class} - #{e.cause.message}
20+
MSG
21+
end
22+
raise
23+
end
24+
25+
def down
26+
remove_foreign_key :spree_user_addresses, :spree_addresses, column: :address_id, null: false
627
end
728
end

core/db/migrate/20250605105424_add_shipping_category_foreign_keys.rb

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,70 @@
22

33
class AddShippingCategoryForeignKeys < ActiveRecord::Migration[7.0]
44
def change
5-
add_foreign_key :spree_products, :spree_shipping_categories, column: :shipping_category_id, null: false
6-
add_foreign_key :spree_shipping_method_categories, :spree_shipping_methods, column: :shipping_method_id, null: false
7-
add_foreign_key :spree_shipping_method_categories, :spree_shipping_categories, column: :shipping_category_id, null: false
5+
# Uncomment the following code to remove orphaned records if the following code fails
6+
#
7+
# say_with_time "Removing orphaned products (no corresponding shipping category)" do
8+
# Spree::Product.left_joins(:shipping_category).where(spree_shipping_category: { id: nil }).delete_all
9+
# end
10+
begin
11+
add_foreign_key :spree_products, :spree_shipping_categories, column: :shipping_category_id, null: false
12+
rescue ActiveRecord::StatementInvalid => e
13+
if e.cause.is_a?(PG::ForeignKeyViolation) || e.cause.is_a?(Mysql2::Error) || e.cause.is_a?(SQLite3::ConstraintException)
14+
Rails.logger.warn <<~MSG
15+
⚠️ Foreign key constraint failed when adding :spree_products => :spree_shipping_categories.
16+
To fix this:
17+
1. Uncomment the code that removes orphaned records.
18+
2. Rerun the migration.
19+
Offending error: #{e.cause.class} - #{e.cause.message}
20+
MSG
21+
end
22+
raise
23+
end
24+
25+
# Uncomment the following code to remove orphaned records if the following code fails
26+
#
27+
# say_with_time "Removing orphaned shipping method categories (no corresponding shipping category)" do
28+
# Spree::ShippingMethodCategory.left_joins(:shipping_category).where(spree_shipping_category: { id: nil }).delete_all
29+
# end
30+
begin
31+
add_foreign_key :spree_shipping_method_categories, :spree_shipping_methods, column: :shipping_method_id, null: false
32+
rescue ActiveRecord::StatementInvalid => e
33+
if e.cause.is_a?(PG::ForeignKeyViolation) || e.cause.is_a?(Mysql2::Error) || e.cause.is_a?(SQLite3::ConstraintException)
34+
Rails.logger.warn <<~MSG
35+
⚠️ Foreign key constraint failed when adding :spree_shipping_method_categories => :spree_shipping_methods.
36+
To fix this:
37+
1. Uncomment the code that removes orphaned records.
38+
2. Rerun the migration.
39+
Offending error: #{e.cause.class} - #{e.cause.message}
40+
MSG
41+
end
42+
raise
43+
end
44+
45+
# Uncomment the following code to remove orphaned records if the following code fails
46+
#
47+
# say_with_time "Removing orphaned shipping method categories (no corresponding shipping method)" do
48+
# Spree::ShippingMethodCategory.left_joins(:shipping_method).where(spree_shipping_method: { id: nil }).delete_all
49+
# end
50+
begin
51+
add_foreign_key :spree_shipping_method_categories, :spree_shipping_categories, column: :shipping_category_id, null: false
52+
rescue ActiveRecord::StatementInvalid => e
53+
if e.cause.is_a?(PG::ForeignKeyViolation) || e.cause.is_a?(Mysql2::Error) || e.cause.is_a?(SQLite3::ConstraintException)
54+
Rails.logger.warn <<~MSG
55+
⚠️ Foreign key constraint failed when adding :spree_shipping_method_categories => :spree_shipping_categories.
56+
To fix this:
57+
1. Uncomment the code that removes orphaned records.
58+
2. Rerun the migration.
59+
Offending error: #{e.cause.class} - #{e.cause.message}
60+
MSG
61+
end
62+
raise
63+
end
64+
end
65+
66+
def down
67+
remove_foreign_key :spree_products, :spree_shipping_categories, column: :shipping_category_id, null: false
68+
remove_foreign_key :spree_shipping_method_categories, :spree_shipping_methods, column: :shipping_method_id, null: false
69+
remove_foreign_key :spree_shipping_method_categories, :spree_shipping_categories, column: :shipping_category_id, null: false
870
end
971
end

0 commit comments

Comments
 (0)