Django将自己传递给models.SET on_delete

时间:2016-04-12 11:23:24

标签: django django-models django-signals

我想修改从数据库中删除的外键值。所以我查看了doc并使用了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设置为cascade)。如果我将on_Delete设置为SET_NULL,它会将外键设置为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()

EDIT -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()            

2 个答案:

答案 0 :(得分:2)

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

您可以从信号接收器获取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(或您配置的任何事件)。

答案 1 :(得分:0)

支持@solarissmoke答案,我认为这是正确的解决方案,但为了给你更多选择,我会发布这个。如果您拥有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