在事务原子内部保存时,使用通用关系保存模型会引发异常

问题描述

我有这个模型,我可以节省应该在稍后阶段计费的费用。由于多个模型可以产生费用,我建立了一个通用关系。

class Fee(models.Model):
    content_type = models.ForeignKey(ContentType,blank=True,null=True,related_name='fees',on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField(null=True,db_index=True)
    content_object = GenericForeignKey()

    class FeeType(models.IntegerChoices):
        SALES_CREDIT = 1,'Sales Credit'
        MANAGEMENT = 2,'Management Fee'
        broKERAGE = 3,'brokerage Fee'
        PLATFORM = 4,'Platform Fee'
        MARKETING = 5,'Marketing Fee'
        disCOUNT = 6,'discount'
        NOFEE = 99,'No Fee'

    fee_type = models.IntegerField(choices=FeeType.choices)

    fee_date = models.DateField(auto_Now_add=True)
    due_date = models.DateField(editable=False)

    creditor = models.ForeignKey('users.Company',on_delete=models.CASCADE,related_name='fee_credit')
    debitor = models.ForeignKey('users.Company',related_name='fee_debit')

    quantity = models.PositiveBigIntegerField()
    rate = models.DecimalField(max_digits=10,decimal_places=4)

    class RateType(models.IntegerChoices):
        PERCENTAGE = 1,'Percentage'
        NOMINAL = 2,'Nominal'

    rate_type = models.IntegerField(choices=RateType.choices,default=1)

    taxes_due = models.BooleanField(default=True)
    including_taxes = models.BooleanField(default=True)
    tax_rate = models.DecimalField(max_digits=4,decimal_places=2)

    total_amount = models.DecimalField(max_digits=12,decimal_places=2,editable=False)
    total_taxes = models.DecimalField(max_digits=12,editable=False)
    total_amount_incl_taxes = models.DecimalField(max_digits=12,editable=False)

    billed = models.BooleanField(default=False)
    paid = models.BooleanField(default=False)

我有一个 celery 任务的函数,它准备将数据保存到所述模型中:

@shared_task
def log_fee(model,fee_type,creditor_pk,debitor_pk,quantity,rate_type,rate=None,taxes_due=True,including_taxes=True,tax_rate=None,fee_date=None):
    from fees.models import Fee
    from users.models import Company

    logger.info(f'{model} {fee_type} {creditor_pk} {debitor_pk} {quantity} {rate_type} {rate} {taxes_due} {including_taxes} {tax_rate}')

    # Get Model Details to link to
    app_label = model.get('app')
    model_name = model.get('object')
    model_pk = model.get('model_pk')

    # Catch fee types that are strings (most likely product related)
    logger.info(type(fee_type))
    if type(fee_type) == str:
        mapping = {'SC': 1,'DC': 6,'MF': 2,'NP': 99}
        fee_type = mapping.get(fee_type)
        if fee_type == 99:
            rate = 0.0
            rate_type = 2

    logger.info(f'{fee_type} {rate} {rate_type}')

    # Get Object
    Object = apps.get_model(app_label=app_label,model_name=model_name)

    # Get Object instance
    try:
        target = Object.objects.get(pk=model_pk)
    except Object.DoesNotExist:
        target = None

    logger.info(target)

    # Get Involved Parties
    debitor = Company.objects.get(pk=debitor_pk)
    creditor = Company.objects.get(pk=creditor_pk)

    logger.info(debitor)
    logger.info(creditor)

    # Clean Fields when fee type == 1
    if fee_type == 1:
        # CALculaTE AMOUNTS AND TAXES
        total_amt = quantity * decimal.Decimal(rate)
        logger.info(total_amt)

        # Total Amount
        if rate_type == 1:
            total_amt = total_amt / 100

        # Set Tax Rate to Companies VAT if not already set.
        if not tax_rate:
            tax_rate = decimal.Decimal(creditor.vat)
            logger.info(tax_rate)

        if taxes_due:
            if including_taxes:
                total_taxes = total_amt - (total_amt / (1 + (tax_rate / 100)))
                total_amt = total_amt - total_taxes

            else:
                total_taxes = total_amt * (tax_rate / 100)
        else:
            total_taxes = 0.0

        total_amount = round(total_amt,2)
        total_taxes = round(total_taxes,2)
        total_amount_incl_taxes = total_amount + total_taxes

        # SET DUE DATE
        if not fee_date:
            fee_date = timezone.Now().date()
        due_date = utils.get_valid_shifted_date(fee_date,days=creditor.payment_term)

        # Save Fee
        fee = Fee(content_object=target,fee_type=fee_type,creditor=creditor,debitor=debitor,quantity=quantity,rate=rate,rate_type=rate_type,taxes_due=taxes_due,including_taxes=including_taxes,tax_rate=tax_rate,due_date=due_date,total_amount=total_amount,total_taxes=total_taxes,total_amount_incl_taxes=total_amount_incl_taxes)

        try:
            with transaction.atomic():
                fee.save()
        except Exception as e:
            logger.info(e)

如果我尝试使用 transaction.atomic() 保存模型,我会收到一条错误消息,指出 'nonetype' object has no attribute 'pk'。如果我省略 transaction.atomic() 语句,一切都会按预期保存。我几乎尝试了一切,但到目前为止没有成功。所有输入参数都是模型期望的格式,正如我所说,没有 transaction.atomic() 保存工作。 尽管如此,我还是觉得忽略错误消息很不舒服。

知道这里发生了什么吗?

PS:我没有在目标模型(我正在测试的模型)上设置 GenericRelation,并且在运行 makemigrations 时,GenericRelation 无论如何都没有列出(这也让我感到困惑)。

我使用 Django 3.2 版和 Postgresql 作为数据库

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)