4

我有类 Invoice,它(简化)具有以下属性:

class Invoice(models.Model)
    number = models.CharField(verbose_name="Number", max_length=16)
    issue_date = models.DateTimeField(verbose_name="Issue date", default=datetime.now)
    total = models.FloatField(verbose_name="Total", blank=True, null=True)

然后,我有类 InvoiceLine,它表示发票可以具有的行/行:

class InvoiceLine(models.Model):
    invoice = models.ForeignKey(Invoice, verbose_name="Invoice")
    description = models.CharField(verbose_name="Description", max_length=64)
    line_total = models.FloatField(verbose_name="Line total")

InvoiceLine 是发票的内联,我想要实现的是,当在管理员中有人保存发票及其行(一个或多个)时,计算发票的总额。我试图通过覆盖方法保存来做到这一点:

class Invoice(models.Model)
    number = models.CharField(verbose_name="Number", max_length=16)
    issue_date = models.DateTimeField(verbose_name="Issue date", default=datetime.now)
    total = models.FloatField(verbose_name="Total", blank=True, null=True)

    def save(self, *args, **kwargs):
         invoice_lines = InvoiceLine.objects.filter(invoice=self.id)
         self.total = 0
         for line in invoice_lines:
             self.total=self.total+line.line_total
         super(Invoice, self).save(*args, **kwargs)

问题是当我在 InvoiceLine 中添加元素时,第一次保存并调用函数时,内联 (InvoiceLine) 中的新元素尚未存储,所以当我这样做时InvoiceLine.objects.filter(invoice=self.id),它们不会被考虑在内。因此,唯一可行的方法是节省两次。我也试过:

def save(self, *args, **kwargs):
    super(Invoice, self).save(*args, **kwargs)
    invoice_lines = InvoiceLine.objects.filter(invoice=self.pk)
    self.total = 0
    for line in invoice_lines:
        self.total=self.total+line.line_total
    super(Invoice, self).save(*args, **kwargs)

但是结果是一样的。任何的想法?提前致谢!

4

3 回答 3

9

最后,在 Django Admin中保存所有内联后,我在 Post Change 对象上找到了它,这对我有很大帮助。关键是在 admin.py 中,我已经有了我的类InvoiceHeaderAdmin(admin.ModelAdmin),但是在保存了所有内联之后,我必须放置三个函数才能修改总属性:这样,invoice_lines = InvoiceLine.objects.filter(invoice_header=obj.pk)以前不能正常工作的查询,现在它完美无缺。周二函数 InvoiceHeaderAdmin 如下:

class InvoiceHeaderAdmin(admin.ModelAdmin):
    inlines = [InvoiceLineInline]
    list_filter = ('format_line','issue_date',)
    list_display = ('number','organization','issue_date','total',)
    fields = ('format_line','organization','issue_date',)

    #the following functions are for calculating the total price of the invoice header based on the lines
    def response_add(self, request, new_object):
        obj = self.after_saving_model_and_related_inlines(new_object)
        return super(InvoiceHeaderAdmin, self).response_add(request, obj)

    def response_change(self, request, obj):
        obj = self.after_saving_model_and_related_inlines(obj)
        return super(InvoiceHeaderAdmin, self).response_change(request, obj)

    def after_saving_model_and_related_inlines(self, obj):

        invoice_lines = InvoiceLine.objects.filter(invoice_header=obj.pk)

        obj.total = 0
        for line in invoice_lines:
            obj.total=obj.total+line.line_total
        obj.save()
        return obj

线索是最后一个函数,所有内联都已保存,现在我可以从对象(发票)计算属性并修改它。

于 2015-07-15T12:58:46.903 回答
1

为了使总数保持最新,我会在需要时对 line_total 进行聚合。这消除了数据库冗余,并进一步防止了多人同时更新发票行可能造成的任何不准确。

class Invoice(models.Model)
    @property
    def total(self):
        result = self.invoiceline_set.aggregate(total=Sum('line_total'))
        return result['total']

解释:

给定一个 Invoice 实例,您可以通过访问 'invoiceline_set'来遵循 InvoiceLine 的 ForeignKey 创建的一对多关系。这实际上相当于InvoiceLine.filter(invoice_id=self.pk).

当您有一组行时,您可以对它们执行 SUM、AVG、COUNT 等数据库聚合,而无需将对象从数据库中拉出,这通常要快得多。

属性装饰器将使此方法充当只读属性。如果您打算在同一个实例上多次调用 total 属性,您实际上应该使用@cached_property以便它只计算一次。

因此,当您键入invoice.total查询时,将对数据库进行查询,该查询将过滤属于 ForeignKey 定义的关系中的所有 InvoiceLine 行,执行 line_total 列的 SUM 计算,并将该值作为键返回给您,值:例如/ ['总数':1234]

于 2015-07-15T07:22:56.930 回答
0

只是对碰巧在这里登陆的其他人的一些更新:您链接到的文章指出,这里引入了一种方法我们可以覆盖并获得相同的结果。使用您的示例:

def save_related(self, request, form, formsets, change):
    super(AjudaCustoProjetoAdmin, self).save_related(request, form, formsets, change)
    obj = form.instance
    invoice_lines = InvoiceLine.objects.filter(invoice_header=obj.pk)

    obj.total = 0
    for line in invoice_lines:
        obj.total=obj.total+line.line_total
    obj.save()
于 2018-08-22T21:12:22.723 回答