如何在Django QuerySet中计算发票总税费与总金额
计算发票总税费与总金额的QuerySet实现
先给出符合常见业务场景的模型字段假设(如果你的实际模型字段不同,对应调整即可):
# 模型示例 class PriceMaster(models.Model): product_name = models.CharField(max_length=100) tax_rate = models.DecimalField(max_digits=5, decimal_places=2) # 存储百分比数值,如13表示13% class Invoice(models.Model): invoice_no = models.CharField(max_length=50, unique=True) created_date = models.DateTimeField(auto_now_add=True) class InvoiceItem(models.Model): invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE, related_name='items') price_master = models.ForeignKey(PriceMaster, on_delete=models.CASCADE) quantity = models.IntegerField() unit_price = models.DecimalField(max_digits=10, decimal_places=2)
1. 单张发票的税费与总金额计算
如果需要单独获取某张发票的汇总数据:
from django.db.models import F, Sum, DecimalField # 定位目标发票 invoice = Invoice.objects.get(invoice_no='INV-001') # 计算该发票的总税费和总金额 invoice_summary = invoice.items.aggregate( total_tax=Sum( F('quantity') * F('unit_price') * (F('price_master__tax_rate') / 100), output_field=DecimalField(max_digits=12, decimal_places=2) ), total_amount=Sum( F('quantity') * F('unit_price') * (1 + F('price_master__tax_rate') / 100), output_field=DecimalField(max_digits=12, decimal_places=2) ) ) # 返回结果示例:{'total_tax': Decimal('130.00'), 'total_amount': Decimal('1130.00')}
2. 批量获取所有发票的汇总数据(带序号)
如果要在页面展示所有发票的序号、发票编号、总税费、总金额,用annotate关联发票与对应项目的汇总值:
from django.db.models import F, Sum, DecimalField, RowNum # 生成带序号的发票汇总列表 invoice_list = Invoice.objects.annotate( total_tax=Sum( F('items__quantity') * F('items__unit_price') * (F('items__price_master__tax_rate') / 100), output_field=DecimalField(max_digits=12, decimal_places=2) ), total_amount=Sum( F('items__quantity') * F('items__unit_price') * (1 + F('items__price_master__tax_rate') / 100), output_field=DecimalField(max_digits=12, decimal_places=2) ), # Django 3.2+支持用RowNum生成数据库层面的序号 serial_no=RowNum() ).values('serial_no', 'invoice_no', 'total_tax', 'total_amount').order_by('invoice_no')
3. 视图传递数据到模板
在视图中把查询结果传给前端模板:
def invoice_summary_view(request): invoice_list = Invoice.objects.annotate( total_tax=Sum( F('items__quantity') * F('items__unit_price') * (F('items__price_master__tax_rate') / 100), output_field=DecimalField(max_digits=12, decimal_places=2) ), total_amount=Sum( F('items__quantity') * F('items__unit_price') * (1 + F('items__price_master__tax_rate') / 100), output_field=DecimalField(max_digits=12, decimal_places=2) ), serial_no=RowNum() ).values('serial_no', 'invoice_no', 'total_tax', 'total_amount').order_by('invoice_no') return render(request, 'invoice_summary.html', {'invoice_list': invoice_list})
4. 模板页面展示
在模板中循环渲染数据:
<table> <thead> <tr> <th>序号</th> <th>发票编号</th> <th>总税费</th> <th>总金额</th> </tr> </thead> <tbody> {% for invoice in invoice_list %} <tr> <td>{{ invoice.serial_no }}</td> <td>{{ invoice.invoice_no }}</td> <td>{{ invoice.total_tax|floatformat:2 }}</td> <td>{{ invoice.total_amount|floatformat:2 }}</td> </tr> {% empty %} <tr> <td colspan="4">暂无发票数据</td> </tr> {% endfor %} </tbody> </table>
注意事项
- 如果
PriceMaster中的税率是小数形式(如0.13表示13%),直接移除计算中的/100部分即可。 - 确保
DecimalField的精度设置足够,避免计算时出现溢出问题。 - 若使用低于Django 3.2的版本,序号可在模板中用
{{ forloop.counter }}代替RowNum。
内容的提问来源于stack exchange,提问作者shyam rathod
相关产品推荐
相关产品推荐

