PG::ForeignKeyViolation 在 Active Admin 中删除 categories/labels 时

PG::ForeignKeyViolation When deleting categories/labels in Active Admin

我正在使用 Active Admin,因此 adminuser 可以 create/delete 类别、标签和产品。

我可以毫无问题地创建 LabelsCategoriesProducts,但是当我想删除标签或类别时,我总是会收到此错误:

PG::ForeignKeyViolation at /admin/labels/2

ERROR:  update or delete on table "labels" violates foreign key constraint "fk_rails_5a55c39b94" on table "products"
DETAIL:  Key (id)=(2) is still referenced from table "products".

如果我试图销毁产品,我会收到这条闪现消息 Product could not be destroyed.

在我看来,当我删除分配给产品的 labelscategories 时出现此错误。

我不确定如何解决它

下面是相关代码

app/admin/product.rb

ActiveAdmin.register Product do

permit_params :title, :description, :image, :price_usd, :price_isl, :category_id, :label_id

index do
    column :title
    column :category
    column :label
    column :created_at

    column :price_isk, :sortable => :price_isl do |product|
        number_to_currency(product.price_isl, :unit => "ISK " , :precision => 0) 
    end
    column :price_euro, :sortable => :price_usd do |product|
        number_to_currency(product.price_usd, :unit => "€ " , :precision => 0)
    end

    actions
 end
end

app/admin/label.rb

ActiveAdmin.register Label do
 permit_params :name
end

app/admin/category.rb

ActiveAdmin.register Category do
 permit_params :name
end

并且在 app/models/product.rb

class Product < ActiveRecord::Base
belongs_to :category
belongs_to :label

has_many :product_item

before_destroy :ensure_not_product_item

    validates :title, :description, presence: true
    validates :price_usd, :price_isl, numericality: {greater_than_or_equal_to: 0.01}
    validates :title, uniqueness: true

 has_attached_file :image, styles: { medium: "500x500#", thumb: "100x100#" }
 validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/

 def ensure_not_product_item
    if product_item.empty?
        return true
    else
        errors.add(:base, 'You have Product Items')
        return false
    end

 end
end

app/models/label.rb

class Label < ActiveRecord::Base
  has_many :products
  has_many :pages
end

app/models/category.rb

class Category < ActiveRecord::Base 
  has_many :products
end

app/models/product_items.rb

class ProductItem < ActiveRecord::Base
 belongs_to :product
 belongs_to :cart
 belongs_to :order


 def total_price_usd
    product.price_usd * quantity
 end

 def total_price_isl
    product.price_isl * quantity
 end

end

您可以在删除类别时将产品 table 中的 label_id 或 category_id 设置为 nil。在我的脑海中,但也许是这样的:

类别模型:

before_destroy :remove_category_from_products

private

def remove_category_from_products
  Product.where(category_id: id).update_all(category_id: nil)  
end 

我的意思是,我对您的应用程序中的业务逻辑了解不多,但这应该可以,这样您就不会再看到该错误...

您想更新 has_many 关联以告诉 ActiveRecord 在您销毁 LabelCategory 时该做什么。听起来您想简单地使关联的 Product 中的 label_idcategory_id 无效,因此:

class Label < ActiveRecord::Base
  has_many :products, :dependent => :nullify
  has_many :pages
end
class Category < ActiveRecord::Base 
  has_many :products, :dependent => :nullify
end

当你销毁一个 Label 时,不确定你想对 Page 做什么,如果你想将它们更新为没有 label_id 那么:

has_many :pages, :dependent => :nullify

如果你想销毁 Page 则:

has_many :pages, :dependent => :destroy

Product 中的 has_many :product_item 类似。如果您想在销毁产品的同时销毁物品,则:

  1. 删除 before_destroy :ensure_not_product_itemensure_not_product_item 方法。
  2. 将关联更新为 has_many :product_items, :dependent => :destroy

如果你想让他们在产品之前手动销毁物品,那么就把你现有的东西留在原地。


潜在的问题是您在数据库中有一个外键约束以确保 products.label_idproducts.category_id 列始终引用有效的 categories.id 值。如果您从 categories 中删除一行(即销毁一个 Category 实例)但 products 中的行仍然引用该类别,那么数据库会抱怨,因为您试图违反外部键约束并在数据库中留下损坏的数据。

数据库中的 FK 是个好主意,因为它可以防止数据库中的引用损坏,但您需要正确清理。