繁体   English   中英

Django QuerySet聚合并加入

[英]Django QuerySet aggregate and join

我是Django的新手,我想弄清楚如何解决此问题。 这是我的模型:

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

class ProductDetail(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE, editable=False)
    price = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal('999.99'))
    available = models.BooleanField(default=True)
    validity_tms = models.DateTimeField(default=timezone.now)

基本上,对于每个产品,我都希望跟踪价格和可用性的变化。 因此,每个产品都有许多ProductDetail。

在需要时,我需要获取每个产品的代码名称可用字段和价格字段,但仅与ProductDetail相关,每个产品的validity_tms最大。

此QuerySet仅包含我需要的部分信息:

ProductDetail.objects.values('product').annotate(max_date=Max('validity_tms'))

我如何也可以检索价格可用字段?

有办法解决吗? 我在模型中做错了吗?

@Todor我需要一个validity_tms最高的tms

然后,您基本上是在寻找每组最多1个查询。

但这不属于通过单个查询完成的ORM的能力

UPDATE

这个问题使我进行了一些深入研究,我刚刚发现我们实际上可以使用来自Django 1.11的新人员建立这样的查询。 我们通过这样做子查询OuterRef ,这里是如何。

latest_product_details = ProductDetail.objects.filter(
    validity_tms=Subquery(
        (ProductDetail.objects
            .filter(product=OuterRef('product'))
            .values('product')
            .annotate(max_date=Max('validity_tms'))
            .values('max_date')[:1]
        )
    )
)

#now you can choose to work with ProductDetail itself:
for product_detail in latest_product_details.select_related('product'):
    product = product_detail.product
    print (product, product_detail)


#or you can work with Product objects and prefetch those latest details
products = Product.objects.my_complex_filtering().prefetch_related(
    models.Prefetch('productdetail_set',
        queryset=latest_product_details,
        to_attr='latest_details'
    )
)
for product in products:
    #get the first element in the list or None if Empty
    last_detail = next(iter(product.latest_details), None)
    print (product, last_detail) 

更新结束。

Django 1.11之前的版本仍然有旧答案

我将为您提供2条查询的替代方法。 这种方法不适用于每个组中有许多对象而又没有机会将组筛选为仅几个元素的情况。 这是因为,当每个组仅需要1个对象时,所有对象都将被加载到内存中。

products = (Product.objects
    .annotate(max_date=Max('productdetail__validity_tms'))
    .prefetch_related(models.Prefetch('productdetail_set',
        #this will fetch all related ProductDetails for a product
        #if one product has many of them, consider further limiting
        #the queryset with some additional filter
        #e.g. only details from the last year or something like that.
        #the idea is to lower the memory footprint, since you need only the first one
        queryset=ProductDetail.objects.order_by('-validity_tms'),
        to_attr='details'
    ))
)

#usage
for product in products:
    print({
        'product': product.id,
        'max_date': product.max_date,
        'detail_price': product.details[0].price if product.details else None,
        'detail_available': product.details[0].available if product.details else None,
    })

试试看:

ProductDetail.objects.values('product', 'price', 'available') \
                     .annotate(max_date=Max('validity_tms'))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM