django - 每月为总和记录设置的 Django 查询集
问题描述
我有以下模型结构:
Product | Price | Quantity | Total* | Date of purchase
Product A | 10 | 1 | 10 | 1/01/2020
Product B | 10 | 2 | 20 | 1/02/2020
*totale 是使用 models.py 中的管理器函数创建的。
我想在我的项目的另一个应用程序中获得每种产品每个月的总和。像这样的东西:
Product | Gen | Feb | Mar | Apr | May | Jun | ....
Product A | 10 | 0 | 0 | 0 | 0 | 0 | ....
Product A | 0 | 20 | 0 | 0 | 0 | 0 | ....
这是我的models.py
class Product(models.Model):
nome= models.CharField()
class MaterialeManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).annotate(
total=F('quantity')*F('price'),
)
def get_monthly_totals(self):
products = dict((p.id, p) for p in Products.objects.all())
return list(
(product, datetime.date(year, month, 1), totale)
for product_id, year, month, totale in (
self.values_list('product__nome', 'date__year', 'date__month')
.annotate(totale=Sum(F('quantity') * F('price')))
.values_list('product__nome', 'date__year', 'date__month', 'totale')
)
class Materiale(models.Model):
product= models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
quantity=models.DecimalField()
price=models.DecimalField()
date=models.DateField()
obejcts=MaterialManager()
但是我尝试使用以下代码来弄清楚但不起作用:
视图.py
def conto_economico(request):
elements = Materiale.objects.all()
context= {
'elements':elements,
}
return render(request, 'conto_economico/conto_economico.html', context)
模板.html
{% for e in elements %}
{{e.totale}}
{% endfor %}
解决方案
好的,我添加了年份,但如果你不想要它可以删除它。
Materiale.objects
.values_list('product__nome', 'date__year', 'date__month')
.annotate(totale=Sum(F('quantity') * F('price')))
.values_list('product__nome', 'date__year', 'date__month', 'totale')
所以第一个 .values_list 触发分组依据,注释添加总和,最后再次使用 values_list 来检索结果。
用法示例;
import datetime
from somewhere import Products
def show_monthly_data(request):
products = dict((p.id, p) for p in Products.objects.all())
defaults = dict((datetime.date(2020, m, 1), 0) for m in range(1, 13))
totals = {}
for product_id, year, month, totale in (
Materiale.objects
.values_list('product__nome', 'date__year', 'date__month')
.annotate(totale=Sum(F('quantity') * F('price')))
.values_list('product__nome', 'date__year', 'date__month', 'totale')
):
product = products[product_id]
if product not in totals:
totals[product] = dict(defaults) # this makes a copy
totals[product][datetime.date(year, month, 1)] = totale
# You could add something to map the products with the totals
return render(request, 'templates/template.html', {}) # etc
# Example to adding it to the Manager
class MaterialeManager(models.Manager):
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).annotate(
total=F('quantity')*F('price'),
)
def get_monthly_totals(self):
products = dict((p.id, p) for p in Products.objects.all())
return list(
(products[product_id], datetime.date(year, month, 1), totale)
for product_id, year, month, totale in (
self.values_list('product__nome', 'date__year', 'date__month')
.annotate(totale=Sum(F('quantity') * F('price')))
.values_list('product__nome', 'date__year', 'date__month', 'totale')
)
编辑:
因此,您遵循该方法并将该方法添加到模型管理器。现在此方法在您的视图中可用。所以下一步是使用这个方法,这样你就可以得到你的元素。
def conto_economico(request):
context= {
'elements': Materiale.objects.get_monthly_totals(),
}
return render(request, 'conto_economico/conto_economico.html', context)
所以该方法返回一个元组列表。但是数据存在两个问题。
- 数据未按产品分组
- 数据在没有售出的月份中没有零值。
问题 1 可以通过添加 order_by 来解决,但这并不能解决第二个问题。因此,我们需要在视图中做的是处理数据,使其在模板中可用。
那么什么是可行的。我们想要一种产品,每个月都有一个包含 12 个值的列表。所以我们可以准备一个零列表并更新我们有数据的零。
def conto_economico(request):
defaults = list(0 for m in range(12))
elements = dict()
for product, date, totale in Materiale.objects.get_monthly_totals():
# First check if product is already part of our elements.
# If not, add the defaults for this product to your elements
if product not in elements:
elements[product] = list(defaults)
# Second, find the index where to update the totale
index = date.month - 1 # jan is one, but on index 0
# Update the value
elements[product][index] = totale
context= {'elements': elements}
return render(request, 'conto_economico/conto_economico.html', context)
所以现在我们可以专注于渲染元素。我们知道elements 是一个字典,其中key 是products,value 是总数列表。
<table>
<tr>
<td>Product</td>
<td>Jan</td>
<td>...</td>
</tr>
{% for product, totals in elements.items %}
<tr>
<td>{{ product.name }}</td>
{% for total in totals %}
<td>{{ total }}</td>
{% endfor %}
<tr>
{% endfor %}
</table>
推荐阅读
- sql - 如何使用蜂巢获取日期差异
- node.js - web3.js 在反应应用程序中提供建议,但不在节点项目中
- sql - 如何将唯一键(列中的唯一值与另一列中的值)复制到多次出现的记录中?
- python - 熊猫中列的唯一对
- c# - 图像在C#的pictureBox中消失
- arrays - 从用户表记录中返回所有值
- c# - Azure Functions .Net5 流式响应 C#
- css - LineHeight 在 ios 和 android 和 web 上表现不同
- python - 为什么 Python 解释器在使用 Pyomo 调用 CBC 求解器后关闭,尽管没有引发错误?
- apache-spark - Databricks 社区版集群无法启动