在我的Django。。。架构?我有一个产品的模型结构,它们的价格和一个产品经历的价格的历史记录。
有点像下面(为这个问题简化了):

class ProductPricing(models.Model):
    price = models.DecimalField(max_digits=8, decimal_places=2,
                                help_text='price in dollars')
    product = models.OneToOneField('app.Product', related_name='pricing',
                                    null=False, on_delete=models.CASCADE)

class Product(models.Model):
    name = models.CharField(max_length=100)
    # .pricing --> related name from 'ProductPricing'

为了保留价格历史,我有一个OldProductPricing模型,它几乎是ProductPricing的副本,但允许每个产品有多个OldProductPricing(s)
class OldProductPricing(models.Model):
    valid_until = models.DateTimeField(null=False)
    product = models.ForeignKey('app.Product', related_name='+',
                                null=False, on_delete=models.CASCADE)

为了确保每次修改或删除产品价格时,我都会尝试在OldProductPricing表中创建一个条目。
为此,我在update或delete时创建了Posgresql触发器:
CREATE TRIGGER price_change_trigger AFTER UPDATE OR DELETE ON app.productpricing
FOR EACH ROW EXECUTE PROCEDURE price_change_trigger_func();

创建插入的触发器函数部分如下:
CREATE OR REPLACE FUNCTION price_change_trigger_func() RETURNS TRIGGER LANGUAGE plpgsql AS $$
    DECLARE
       retval RECORD;
    BEGIN
        IF (TG_OP = 'DELETE') THEN
            retval := OLD;
        ELSE
            retval := NEW;
        END IF;

        RAISE LOG 'Inserting in old_prices table copy of Pricing record ID=%% for product_id=%% because of %%', OLD.id, OLD.product_id, TG_OP;
        old_to_insert.product_id := OLD.product_id;
        old_to_insert.price := OLD.price;
        old_to_insert.valid_until := now() at time zone 'utc';
        old_to_insert.id := nextval('app_oldproductpricing_id_seq');

        IF EXISTS(SELECT 1 FROM app_product WHERE app_product.id=old_to_insert.product_id)) THEN
            INSERT INTO app_oldproductpricing VALUES(old_to_insert.*);
        END IF;
        RETURN retval;
    EXCEPTION WHEN others THEN
        -- Do nothing...
        RAISE WARNING 'Got error on %% %% %%', TG_OP, SQLERRM, SQLSTATE;
        RETURN NULL;
    END;

在大多数情况下,它工作得很好,但是当Product被删除,并且CASCADES开始发生(从产品到->产品定价)时,EXISTS(SELECT 1 FROM app_product WHERE app_product.id=old_to_insert.product_id))似乎不起作用:即使我检查了Product存在,我得到:
insert or update on table "app_oldproductpricing" violates foreign key constraint "app_oldproductpricing_product_id_bb078367_fk_app_product_id"
DETAIL:  Key (product_id)=(5) is not present in table "app_product".

我认为发生的情况是,触发器在要删除Product-->ProductPricing的事务中运行,而EXISTS检查在实际删除Product之前发生??(我不太确定,不过。。。这只是一种怀疑),因此创建了一个新的OldProductPricing实例,但之后,thisProduct行引用被删除,使数据库处于不一致的状态。
我的理论正确吗?最重要的是。。。有什么办法解决这个问题吗?
提前谢谢你。任何暗示或建议将不胜感激。
注:我用的是Django 1.11和Postgresql 9.5。

最佳答案

这个问题是由一个“变异表”引起的,当DJANGO模拟ON-CASCADE时,首先删除表“ProductPricing”上的行,然后“OldProductPricing”上的insert在“Product”中的一行上,该行将消失。你还有其他选择:
删除触发器并管理Product.save()和ProductPricing.save()中的逻辑,然后在@Delete=CASCADE by on@Delete=dou NOTHING上进行更改。

日志表(“OldProductPricing”)应该永远保存数据,并显示从“Product”到“Product”的所有历史更改,因此您应该删除从“OldProductPricing”到“Product”的外键。

10-04 11:20