【问题标题】:Django related field always throws integrity error on production onlyDjango 相关字段总是在生产中抛出完整性错误
【发布时间】:2016-04-10 16:14:42
【问题描述】:

我的 django 应用中有以下两个模型:

class BaseModel(models.Model):
    """
    Base model
    """
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)
    created = models.DateTimeField(auto_now_add=True, null=True)
    modified = models.DateTimeField(auto_now=True, null=True)

    class Meta:
        abstract = True

class BaseBillingModel(BaseModel):
    """
    Base billing model with owner field and real_owner method (may reffer to  Customer or Profile)
    """
    owner = models.UUIDField() # uuid of Profile OR Customer

    @property
    def real_owner(self):
        if self.type == 'customer':
            return Customer.objects.get(uuid=self.owner)
        if self.type == 'user':
            return Profile.objects.get(uuid=self.owner)

    class Meta:
        abstract = True


class Invoice(BaseBillingModel):
    type = models.CharField(_('Invoice type'), max_length=16,      choices=INVOICE_PAYMENT_TYPES, default='user')
    title = models.CharField(_("Title"), max_length=128, default="")
    description = models.CharField(_("Description"), max_length=512, default="")
    paid = models.BooleanField(_("Paid"), default=False)
    cost = models.DecimalField(_("Total cost"), decimal_places=2, max_digits=10, default=Decimal("0"))
    public = models.BooleanField(default=True)


    def mark_paid(self):
        self.paid = True
        self.save()


class InvoiceItem(BaseModel):
    invoice = models.ForeignKey(Invoice, null=True, blank=True)
    cost = models.DecimalField(_("Cost"), decimal_places=2, max_digits=10, default=Decimal("0"))
    quantity = models.IntegerField(_("Quantity"), default=1)
    title = models.CharField(_("Title"), max_length=128, default="")
    description = models.CharField(_("Description"), max_length=512, default="")

它基本上是一个包含许多发票项目的发票模型。 我使用表单集在发票中动态添加/删除项目。 当我发布网络表单时,我会遍历项目并计算总成本,然后保存发票。

查看代码:

if request.method == 'POST':
    invoice_form = InvoiceForm(request.POST)
    formset = InvoiceItemFormset(request.POST)

    total_forms = int(request.POST.get('form-TOTAL_FORMS'))

    for x in range(total_forms):
        identifier = "form-{x}-DELETE".format(x=x)
        if identifier in request.POST:
            deleted_items.append(x)


    if request.POST.get('type') == 'customer':
        invoice_form.fields['owner_choice'].choices = [(o.uuid.urn[9:],o.company_name) for o in Customer.objects.filter()]

    if invoice_form.is_valid() and formset.is_valid():
        invoice = invoice_form.save(commit=False)
        invoice.owner = invoice_form.cleaned_data.get('owner_choice')
        cost = Decimal(0)
        for form in formset:

            if form not in formset.deleted_forms:
                item = form.save(commit=False)
                item.invoice = invoice
                item.save()
                cost += item.cost * item.quantity
        invoice.cost = cost
        invoice.save()

它在我的本地环境 (MariaDB) 上运行良好,正在为所有项目创建发票。 但是,在舞台/生产服务器上,我使用 MySQL 还是 PostgreSQL 并不重要 - 我总是会收到如下错误: (postgresql)

IntegrityError at /staff/billing/new_invoice
insert or update on table "core_invoiceitem" violates foreign key constraint "core_invoiceit_invoice_id_165c623e80fbe59c_fk_core_invoice_uuid"
DETAILS:  Key (invoice_id)=(bb83ff7a-9428-458c-909f-6ab4fa24a1b3) is not present in table "core_invoice".

我不知道如何解决这个问题。有什么想法吗?

【问题讨论】:

    标签: python mysql django postgresql


    【解决方案1】:

    在保存发票项目之前,您必须先保存它引用的发票。目前,您的阶段/生产服务器在保存发票项目之后但在保存发票之前检查外键约束,因此您会收到完整性错误。

    由于您使用的是 uuid 字段,Python 正在创建主键而不是您的数据库。因此,您可以通过使用 atomic 装饰器来防止错误。当您使用 atomic 装饰器时,数据库会检查原子块末尾的外键约束,此时发票已保存。

    from django.db import transaction
    
    transaction.atomic()
    def my_view(request):
    

    如果这不起作用,您需要确保在保存发票项目之前已保存发票(不带 commit=False)。您可以保存发票项目和发票表格(都带有commit=False),计算成本,保存发票,最后保存发票项目。

    【讨论】:

    • 但我需要先在发票上设置总成本。我的发票模型(保存后)上有一个信号,它会向发票所有者发送一封包含总成本的电子邮件。我可以在不触发信号的情况下保存发票吗?另外,为什么它在我的本地数据库上工作?
    • 您可以在将发票保存到数据库之前计算成本。听起来 MariaDB 可能没有检查外键约束(例如 MyISAM 引擎没有)。我对 MariaDB 没有任何经验,所以我无法告诉您为什么在您的开发环境中没有收到错误。如果您已将信号附加到save(),我认为您不能在不触发的情况下调用save()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-22
    • 2019-10-12
    • 2022-12-21
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    相关资源
    最近更新 更多