如何在 models.py 中的 save 方法中获取内联对象
How to get the inline objects in the save method in models.py
我有 class 发票,它(简化)具有以下属性:
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)
然后,我有 class InvoiceLine,代表发票可以具有的 line/lines:
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 是 Invoice 的内联,我想要实现的是,当有人在管理员中保存发票及其行(一个或多个)时,计算发票总额。我试图通过覆盖方法 save:
来做到这一点
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)
但结果相同。任何的想法?
提前致谢!
为了使总数保持最新,我会在需要时对 line_total 进行汇总。这消除了数据库冗余,并进一步防止了多人同时更新发票行可能造成的任何不准确。
class Invoice(models.Model)
@property
def total(self):
result = self.invoiceline_set.aggregate(total=Sum('line_total'))
return result['total']
解释:
给定一个 Invoice 实例,您可以遵循 InvoiceLine 的 ForeignKey 创建的一对多关系 by accessing 'invoiceline_set'。这实际上相当于InvoiceLine.filter(invoice_id=self.pk)
。
当您有一组行时,您可以对它们执行 SUM、AVG、COUNT 之类的数据库 aggregations,而无需将对象从数据库中取出来执行此操作,这通常要快得多。
property decorator will make this method act as a read-only attribute. If you intend to call the total property multiple times on the same instance, you should actually use @cached_property所以只计算一次。
因此,当您键入 invoice.total
时,将对数据库进行查询,该查询将过滤属于 ForeignKey 定义的关系中的所有 InvoiceLine 行,执行 [=31= 的 SUM 计算] 列,并且 return 那个值给你作为键,值:例如/ ['total': 1234]
我终于在 post Change object after saving all inlines in Django Admin 上找到了它,这对我帮助很大。关键在 admin.py 中,我已经有了我的 class InvoiceHeaderAdmin(admin.ModelAdmin)
,但是我不得不放置三个函数以便在保存所有内联后修改总属性:这样, 查询 invoice_lines = InvoiceLine.objects.filter(invoice_header=obj.pk)
之前效果不好,现在完美了。 Tue函数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
线索是最后一个函数,所有内联都已保存,现在我可以从对象(发票)计算属性并修改它。
只是对碰巧来到这里的其他人的一些更新:我们可以覆盖并获得相同结果的 article you linked to stated that there's a method introduced here。使用您的示例:
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()
我有 class 发票,它(简化)具有以下属性:
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)
然后,我有 class InvoiceLine,代表发票可以具有的 line/lines:
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 是 Invoice 的内联,我想要实现的是,当有人在管理员中保存发票及其行(一个或多个)时,计算发票总额。我试图通过覆盖方法 save:
来做到这一点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)
但结果相同。任何的想法? 提前致谢!
为了使总数保持最新,我会在需要时对 line_total 进行汇总。这消除了数据库冗余,并进一步防止了多人同时更新发票行可能造成的任何不准确。
class Invoice(models.Model)
@property
def total(self):
result = self.invoiceline_set.aggregate(total=Sum('line_total'))
return result['total']
解释:
给定一个 Invoice 实例,您可以遵循 InvoiceLine 的 ForeignKey 创建的一对多关系 by accessing 'invoiceline_set'。这实际上相当于InvoiceLine.filter(invoice_id=self.pk)
。
当您有一组行时,您可以对它们执行 SUM、AVG、COUNT 之类的数据库 aggregations,而无需将对象从数据库中取出来执行此操作,这通常要快得多。
property decorator will make this method act as a read-only attribute. If you intend to call the total property multiple times on the same instance, you should actually use @cached_property所以只计算一次。
因此,当您键入 invoice.total
时,将对数据库进行查询,该查询将过滤属于 ForeignKey 定义的关系中的所有 InvoiceLine 行,执行 [=31= 的 SUM 计算] 列,并且 return 那个值给你作为键,值:例如/ ['total': 1234]
我终于在 post Change object after saving all inlines in Django Admin 上找到了它,这对我帮助很大。关键在 admin.py 中,我已经有了我的 class InvoiceHeaderAdmin(admin.ModelAdmin)
,但是我不得不放置三个函数以便在保存所有内联后修改总属性:这样, 查询 invoice_lines = InvoiceLine.objects.filter(invoice_header=obj.pk)
之前效果不好,现在完美了。 Tue函数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
线索是最后一个函数,所有内联都已保存,现在我可以从对象(发票)计算属性并修改它。
只是对碰巧来到这里的其他人的一些更新:我们可以覆盖并获得相同结果的 article you linked to stated that there's a method introduced here。使用您的示例:
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()