【问题标题】:Django query set for sums records each month每月为总和记录设置的 Django 查询集
【发布时间】:2020-08-08 23:21:13
【问题描述】:

我有以下模型结构:

 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()

但我尝试以下代码来弄清楚但不起作用:

views.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 %}

【问题讨论】:

    标签: django django-models django-rest-framework django-forms django-views


    【解决方案1】:

    好的,我添加了年份,但如果您不想要,可以将其删除。

    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 触发 group by,annotate 加上 sum,最后再次 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. 数据未按产品分组
    2. 对于没有售出任何东西的月份,数据没有零值。

    问题 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 是一个字典,键是产品,值是总计列表。

    <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>
    

    【讨论】:

    • 谢谢现在试试。但是我应该在我的视图中添加这段代码吗?或者在模型中。以及如何在查询集中提取这些值?
    • 取决于你想如何使用它。在多个视图中使用它?您可以将其添加到查询集中,否则将其添加到您的视图中。如何使用它,这取决于。例如,如果没有记录,这不会返回任何值。我会用一个例子来更新答案。
    • 哦,谢谢 :) 但是在模型中如何添加它?考虑到 models.py 在另一个应用程序中
    • 添加了两个示例。一种如何在您的视图中使用它。另一个关于如何将其添加到模型管理器的内容。
    • 非常感谢您详尽的回答。但我是 django 的新手,我不明白如何在我的 template.html 中找出你的代码。我已按照您的建议编辑了我的答案,但我的模板代码没有利用我的观点找出任何东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 2021-02-26
    • 2012-07-16
    • 1970-01-01
    • 2019-06-08
    • 2020-09-27
    相关资源
    最近更新 更多