Django 将自己传递给 models.SET on_delete

Django pass self to models.SET on_delete

我想修改从数据库中删除的外键值。所以我查看了文档并使用了 on_delete=models.SET(foo) 方法。 https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.SET

这是我的模型定义

class OrderLine(models.Model):
    product = models.ForeignKey(Product, on_delete=models.SET(getDuplicateProduct), null=True)
    quantity = models.PositiveSmallIntegerField(default=1)
    finalPricePerUnit = models.PositiveIntegerField()
    order = models.ForeignKey(Order, on_delete=models.PROTECT)
    dateCreated = models.DateTimeField(auto_now=False, auto_now_add=True)

这是我在删除时调用的方法

def getDuplicateProduct(orderline):
    productToDelete = orderline.product
    # some logic to generate duplicate copy and returning it

但是这里的问题是我不能将参数传递给这个方法,这就是为什么我不知道哪个产品被删除了。我还尝试使用此答案中指定的信号 django model on_delete pass self to models.SET()

我也尝试过使用信号,但也没有用。我似乎无法为此找到合适的解决方案。 如果有人知道如何实现这一点,请告诉我。

编辑

这是我在信号中使用的代码

@receiver(pre_delete, sender=Product)
def getDuplicateProduct(sender, **kwargs):
    product = kwargs['instance']
    orderlines = product.orderline_set.all()
    #further processing

现在的问题是 django 也试图删除我的订单(默认 on_delete 设置为级联)。如果我将 on_Delete 设置为 SET_NULL,它会将外键设置为空。

编辑-2 这是我使用的代码

@receiver(pre_delete, sender=Product)
def getDuplicateProduct(sender, **kwargs):
    product = kwargs['instance']
    orderlines = product.orderline_set.all()
    product.name = product.name + ' ' + product.get_type_display()
    newProduct = deepcopy(product)
    newProduct.name = product.name + ' ' + product.get_type_display()
    newProduct.pk=None
    newProduct.id=None
    newProduct.save()
    product.duplicateProductId = newProduct.id
    product.old_orderlines = orderlines
    product.save()


@receiver(post_delete, sender=Product)
def handlePostDelete(sender, **kwargs):
    product = kwargs['instance']
    newProduct = Product.objects.get(id=product.duplicateProductId)
    for orderline in product.old_orderlines:
        orderline.product = newProduct
        orderline.save()

编辑-3 为了完整起见,发布完整的实现。

@receiver(pre_delete, sender=Product)
def handlePreDelete(sender, **kwargs):
    product = kwargs['instance']
    orderlines = product.orderline_set.all()
    shouldCreate=False
    for orderline in orderlines:
        if orderline.order.status>1:
            shouldCreate=True
    product.shouldCreate = shouldCreate
    if shouldCreate:
        product.old_orderlines = orderlines
        product.save()
    else:
        product.save()
        return None


@receiver(post_delete, sender=Product)
def handlePostDelete(sender, **kwargs):
    product = kwargs['instance']
    shouldCreate = product.shouldCreate
    if shouldCreate:
        newProduct = deepcopy(product)
        newProduct.name = product.name + ' ' + product.get_type_display()
        newProduct.pk=None
        newProduct.id=None
        newProduct.save()
        # Do whatever you want with product.old_orderlines
        for orderline in product.old_orderlines:
            orderline.product = newProduct
            orderline.save()            

信号是执行此操作的正确方法。

您可以从信号接收器获取OrderLine

@receiver(pre_delete, sender=Product)
def getDuplicateProduct(sender, **kwargs):
    product = kwargs['instance']
    orderlines = product.orderline_set.all()
    # orderlines contains all the OrderLines foreign keyed to the product.

orderlines 是一个可以迭代的查询集或 update in bulk.

编辑

原来上面建议的方法行不通,因为在 pre_delete 信号触发时 Django 已经确定它需要用 on_delete 处理哪些相关模型,并将覆盖这些变化。

这种方法可行,尽管有点笨拙:

首先,在 pre_delete 接收器中: 从复制导入复制

@receiver(pre_delete, sender=Product)
def handlePreDelete(sender, **kwargs):
    product = kwargs['instance']
    # Store the OrderLines as a property of the object
    # Have to copy it otherwise it will be empty later
    product.old_orderlines = copy(product.orderline_set.all())

然后,在 post_delete 接收器中:

@receiver(post_delete, sender=Product)
def handlePostDelete(sender, **kwargs):
    product = kwargs['instance']
    # Do whatever you want with product.old_orderlines
    for line in product.old_orderlines:
        # ... 

在这两个事件之间,Django 将在 OrderLines 上执行 SET_NULL(或您配置的任何内容)。

支持@solarissmoke 的回答,我认为这是正确的解决方案,但为了给您更多选择,我会 post 这个。如果你有 product 实例,你可以有它的 orderlines。例如,如果您像这样将 related_name 添加到 OrderLine.product

class OrderLine(models.Model):
    product = models.ForeignKey(Product, related_name='orderlines')

然后你可以在你的信号中这样做:

@receiver(pre_delete, sender=Product)
def getDuplicateProduct(sender, **kwargs):
    product = kwargs['instance']
    orderlines = product.orderlines.all()

只是另一种方法,获得 orderlines.