按外键分组获取每个组的最新Django记录

7

假设我有这样的模型:

class Product(models.Model):
  name = models.CharField(max_length=50)

class Sale(models.Model):
  product = models.ForeignKey(Product)
  sale_date = models.DateField()

我想要获取每个产品的最新销售。最新指的是最新的销售日期。同时,在同一个查询中,我还想要获取每个产品的销售数量。
从SQL角度来看,我需要按product_id进行分组,以计算每个产品的销售量(即每个组的长度)。也许还需要按日期排序(在每个组内部)才能仅获取最新的产品销售情况。
通过django ORM,我意识到我需要以某种方式使用annotate(),也许结合values()使用。但我仍然没有搞清楚如何做到这一点。
假设我有这个products表:
| id |   name    |    
===================
| 1  | Book      |  
| 2  | Telephone |

还有这个销售表格:

| id | product_id | sale_date |
=================================
| 1  |      1     | 05-02-2015 |
| 2  |      2     | 04-02-2015 |
| 3  |      2     | 03-02-2015 |
| 4  |      1     | 06-02-2015 |
| 5  |      1     | 01-02-2015 |

我希望获得以下输出:
| product_id |    name   | sale_date  | num_sales |
====================================================
|     1      | Book      | 06-02-2015 |     3     |
|     2      | Telephone | 04-02-2015 |     2     |

由于06-02-2015是product_id=1(图书)的最新销售日期,而04-02-2015是product_id=2(电话)的最新日期。

1个回答

1
from django.db.models import Count, Max
query = Product.objects.annotate(num_sales=Count('sale'))
query = query.annotate(latest_sale_date=Max('sale__sale_date'))
for prod in query.all():
    print (prod.pk, prod.name, prod.latest_sale_date, prod.num_sales)

你会得到如下输出:
(1, u'Book', datetime.date(2015, 6, 2), 3)
(2, u'Telephone', datetime.date(2015, 4, 2), 2)

类似于您在问题中期望的输出。请注意,您传递给annotate的任何kwarg都将成为查询结果上的属性。

谢谢。它确实有效。但我想了解一些事情。我知道annotate()为产品对象创建一个新属性。但是你如何使用像Count()这样的函数?你使用了一个不在产品模型中的参数('sale'),所以我很困惑。其次,查询末尾的all()调用是否重要? - user3599803
我在Django 1.7的新测试应用程序中使用您问题中发布的模型进行了测试。我可能错了,但我相信那里的“sale”只是Django自动为您填充FK关系的一部分,并且它只是将类名小写以生成它。我认为all不是必需的,因为QuerySet对象是可迭代的,但是最近我一直在编写太多的SQLAlchemy。我不认为这会有什么损失,让它存在那里。 - Two-Bit Alchemist
当我测试查看connection.queries变量生成的查询时,我看到一个带有两个列的group by子句的查询。我认为这是一个效率低下的查询? - user3599803

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接