简体   繁体   中英

PG::ForeignKeyViolation: ERROR after changing a has_and_belongs_to_many association in rails to a has_many :through association

I recently switched a has_and_belongs_to_many (HABTM) association to a has_many:through association and now I have a lot of failing tests due to violating foreign key constraints. Here's an example test failure message:

DRb::DRbRemoteError: PG::ForeignKeyViolation: ERROR:  update or delete on table "choices" violates foreign key constraint "fk_rails_d6ffbc38aa" on table "selections"
        DETAIL:  Key (id)=(506907318) is still referenced from table "selections"

Here's the form that creates the association. 我的表格截图

Here are my models with the relevant associations...

models/variant.rb

class Variant < ApplicationRecord    
  has_many :selections
  has_many :choices, through: :selections
end

models/choice.rb

class Choice < ApplicationRecord
  has_many :variants, through: :selections

  belongs_to :option
end

models/selection.rb

class Selection < ApplicationRecord
  belongs_to :choice
  belongs_to :variant
end

And the relevant schemas:

create_table "variants", force: :cascade do |t|
  t.string "sku"
  t.datetime "created_at", precision: 6, null: false
  t.datetime "updated_at", precision: 6, null: false
  t.bigint "account_id"
  t.bigint "product_id"
  t.index ["account_id"], name: "index_variants_on_account_id"
  t.index ["product_id"], name: "index_variants_on_product_id"
end

create_table "choices", force: :cascade do |t|
  t.string "name"
  t.datetime "created_at", precision: 6, null: false
  t.datetime "updated_at", precision: 6, null: false
  t.bigint "option_id"
  t.bigint "account_id"
  t.index ["account_id"], name: "index_choices_on_account_id"
  t.index ["option_id"], name: "index_choices_on_option_id"
end

create_table "selections", force: :cascade do |t|
  t.bigint "choice_id"
  t.bigint "variant_id"
  t.datetime "created_at", precision: 6
  t.datetime "updated_at", precision: 6
  t.index ["choice_id"], name: "index_selections_on_choice_id"
  t.index ["variant_id"], name: "index_selections_on_variant_id"
end

Everything works fine in the browser. It's just my tests that are failing and all of the failing tests have this same foreign key error.

I don't want to start adding associations and foreign keys to my models just to get my tests passing since the expected behavior is working in the browser.

I'm using fixtures for my tests. Is this the problem? Is there something about my fixtures that could be causing this error? FYI, I want to continue using fixtures and not switch the app to factories.

test/fixtures/variants.yml

small_t_shirt_in_red:
  id: 1
  account: fuzz
  product: t_shirt
  sku: FUZZ-T-001
  choices: small, red

medium_generic_gadget_in_blue:
  id: 2
  account: generic_gadgets
  product: gadget
  sku: GENERIC-001
  choices: medium, blue


test/fixtures/choices.yml

small:
  name: Small

medium:
  name: Medium

large:
  name: Large

red:
  name: Red

blue:
  name: Blue


test/fixtures/selections.yml

small_t_shirt_selection:
  choice_id: 1
  variant_id: 1

red_t_shirt_selection:
  choice_id: 4
  variant_id: 1

medium_generic_selection:
  choice_id: 2
  variant_id: 2

blue_generic_selection:
  choice_id: 5
  variant_id: 2

Try dropping and recreating your test DB:

RAILS_ENV=test rails db:drop db:create

Maybe the test DB has a foreign key constraint in it that the main/dev DB doesn't.

Update

I figured out the problem, so posting the solution here in case anyone else needs it.

Here are the fixtures that worked:

test/fixtures/variants.yml

small_t_shirt_in_red:
  account: fuzz
  product: t_shirt
  sku: FUZZ-T-001
  choices: small, red

medium_generic_gadget_in_blue:
  account: generic_gadgets
  product: gadget
  sku: GENERIC-001
  choices: medium, blue


test/fixtures/choices.yml

small:
  name: Small

medium:
  name: Medium

large:
  name: Large

red:
  name: Red

blue:
  name: Blue


test/fixtures/selections.yml

red_t_shirt_selection:
  choice: red
  variant: small_t_shirt_in_red

medium_generic_selection:
  choice: medium
  variant: medium_generic_gadget_in_blue

Then, I needed to update my models to this:

models/variant.rb

class Variant < ApplicationRecord
  has_many :selections, dependent: :destroy
  has_many :choices, through: :selections
end


models/choice.rb

class Choice < ApplicationRecord
  has_many :selections, dependent: :destroy
  has_many :variants, through: :selections

  belongs_to :option
end


models/selection.rb

class Selection < ApplicationRecord
  belongs_to :choice
  belongs_to :variant
end

I was missing the has_many:selections, dependent: :destroy on both the Variant and Choice models.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM