Skip to content

Commit f711de0

Browse files
committed
Add foreign key constraint point to country table
This adds database-level foreign key constraints to tables that reference the countries table. If trying to delete a country, and there are addresses referencing that country, we want to restrict that. Addresses are immutable and must stay valid. If there are prices referencing the country, the same applies, we want to restrict. If the only thing holding us back from deleting a country is states, we can delete the state records as well.
1 parent 620ff14 commit f711de0

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# frozen_string_literal: true
2+
3+
class AddCountryForeignKeys < ActiveRecord::Migration[7.0]
4+
def up
5+
# Uncomment the following code to remove orphaned records if this migration fails
6+
#
7+
# say_with_time "Removing orphaned states (no corresponding country)" do
8+
# Spree::State.left_joins(:country).where(spree_countries: { id: nil }).delete_all
9+
# end
10+
11+
begin
12+
add_foreign_key :spree_states, :spree_countries, column: :country_id, null: false, on_delete: :cascade
13+
rescue ActiveRecord::StatementInvalid => e
14+
if e.cause.is_a?(PG::ForeignKeyViolation) || e.cause.is_a?(Mysql2::Error) || e.cause.is_a?(SQLite3::ConstraintException)
15+
Rails.logger.warn <<~MSG
16+
⚠️ Foreign key constraint failed when adding :spree_states => :spree_countries.
17+
To fix this:
18+
1. Uncomment the code that removes orphaned records.
19+
2. Rerun the migration.
20+
Offending error: #{e.cause.class} - #{e.cause.message}
21+
MSG
22+
end
23+
raise
24+
end
25+
26+
# Uncomment the following code to remove orphaned records if this migration fails
27+
#
28+
# say_with_time "Updating orphaned addresses (no corresponding country) to use default country" do
29+
# Spree::Address.left_joins(:country).where(spree_countries: { id: nil }).update_all(country: Spree::Country.default)
30+
# end
31+
32+
begin
33+
add_foreign_key :spree_addresses, :spree_countries, column: :country_id, null: false, on_delete: :restrict
34+
rescue ActiveRecord::StatementInvalid => e
35+
if e.cause.is_a?(PG::ForeignKeyViolation) || e.cause.is_a?(Mysql2::Error) || e.cause.is_a?(SQLite3::ConstraintException)
36+
Rails.logger.warn <<~MSG
37+
⚠️ Foreign key constraint failed when adding :spree_addresses => :spree_countries.
38+
To fix this:
39+
1. Uncomment the code that removes orphaned records.
40+
2. Rerun the migration.
41+
Offending error: #{e.cause.class} - #{e.cause.message}
42+
MSG
43+
end
44+
raise
45+
end
46+
# Uncomment the following code to remove orphaned records if this migration fails
47+
#
48+
# say_with_time "Deleting orphaned prices (country ID without corresponding country)" do
49+
# Spree::Price.where.not(country_iso: nil).left_joins(:country).where(spree_countries: { iso: nil }).update_all(country_iso: Spree::Config.default_country_iso)
50+
# end
51+
52+
begin
53+
add_foreign_key :spree_prices, :spree_countries, column: :country_iso, primary_key: :iso, null: true, on_delete: :restrict
54+
rescue ActiveRecord::StatementInvalid => e
55+
if e.cause.is_a?(PG::ForeignKeyViolation) || e.cause.is_a?(Mysql2::Error) || e.cause.is_a?(SQLite3::ConstraintException)
56+
Rails.logger.warn <<~MSG
57+
⚠️ Foreign key constraint failed when adding :spree_prices => :spree_countries.
58+
To fix this:
59+
1. Uncomment the code that removes orphaned records.
60+
2. Rerun the migration.
61+
Offending error: #{e.cause.class} - #{e.cause.message}
62+
MSG
63+
end
64+
raise
65+
end
66+
end
67+
68+
def down
69+
remove_foreign_key :spree_states, :spree_countries, column: :country_id, null: false, on_delete: :cascade
70+
remove_foreign_key :spree_addresses, :spree_countries, column: :country_id, null: false, on_delete: :restrict
71+
remove_foreign_key :spree_prices, :spree_countries, column: :country_id, null: true, on_delete: :restrict
72+
end
73+
end

0 commit comments

Comments
 (0)