【问题标题】:Django ORM annotate with subqueryDjango ORM 使用子查询进行注释
【发布时间】:2018-08-26 18:15:02
【问题描述】:

我有以下型号:

class Store(models.Model):
   name = models.CharField()

class Goods(models.Model):
   store = models.ForeigKey(Store, on_delete=models.CASCADE)
   total_cost = models.DecimalField()
   different values ...

所以,我根据参数过滤了所有商品,现在我的目标是从每个商店中获得一件商品,在这家商店的所有商品中价格最低

stores = Store.objects.all() - all stores
goods = Good.objects.filter(..) - filter goods

goods.annotate(min_price=Subquery(Min(stores.values('goods__total_cost'))))                                                                   

我尝试了类似的方法,但出现错误:

AttributeError: 'Min' object has no attribute 'query'

【问题讨论】:

    标签: python django django-orm


    【解决方案1】:

    我认为在您的上下文中,您需要 Group By 功能而不是 Django 注释,
    来自this SO answer,

    >>> q = Book.objects.annotate(num_authors=Count('authors'))
    >>> q[0].num_authors
    2
    >>> q[1].num_authors
    1
    

    q 是书籍的查询集,但每本书都标注了作者的数量。 也就是说,如果你 annotate 你的 goods 查询集,他们不会给你一些排序/过滤的对象集。它将仅使用新字段 min_price 进行注释。

    所以我建议您按照以下方式进行 Group By 操作

    from django.db.models import Min
    
    result = Goods.objects.values('store').annotate(min_val=Min('total_cost'))
    


    示例

    In [2]: from django.db.models import Min
    
    In [3]: Goods.objects.values('store').annotate(min_val=Min('total_cost'))
    Out[3]: <QuerySet [{'store': 1, 'min_val': 1}, {'store': 2, 'min_val': 2}]>
    
    In [6]: Goods.objects.annotate(min_val=Min('total_cost'))
    Out[6]: <QuerySet [<Goods: Goods object>, <Goods: Goods object>, <Goods: Goods object>, <Goods: Goods object>, <Goods: Goods object>]>
    
    In [7]: Goods.objects.annotate(min_val=Min('total_cost'))[0].__dict__
    Out[7]:
    {'_state': <django.db.models.base.ModelState at 0x7f5b60168ef0>,
     'id': 1,
     'min_val': 1,
     'store_id': 1,
     'total_cost': 1}
    
    In [8]: Goods.objects.annotate(min_val=Min('total_cost'))[1].__dict__
    Out[8]:
    {'_state': <django.db.models.base.ModelState at 0x7f5b6016af98>,
     'id': 2,
     'min_val': 123,
     'store_id': 1,
     'total_cost': 123}
    



    UPDATE-1
    我认为,这不是一个好主意,可能会出现一些优化问题,但您可以尝试

    from django.db.models import Min
    
    store_list = Store.objects.values_list('id', flat=True)  # list of id's od Store instance
    result_queryset = []
    for store_id in store_list:
        min_value = Goods.objects.filter(store_id=store_id).aggregate(min_value=Min('total_cost'))
        result_queryset = result_queryset|Goods.objects.filter(store_id=store_id, total_cost=min_value)
    



    UPDATE-2

    我认为我的Update-1 部分存在大量性能问题,因此我为您的问题找到了一个可能的答案,即

    goods_queryset = Goods.objects.filter(**you_possible_filters)
    result = goods_queryset.filter(store_id__in=[good['store'] for good in Goods.objects.values('store').annotate(min_val=Min('total_cost'))])
    

    【讨论】:

    • 能否在查询集中获取 Goods 的实例?
    • update-2 工作不正确我想在数据库中只用一个复杂的查询找到所有产品,而不用 python 计算一些东西,因为现在我在 python 和响应中寻找合适的产品耗时 0.6 秒。我希望将来响应时间会增加..
    猜你喜欢
    • 1970-01-01
    • 2020-01-26
    • 2019-10-13
    • 2019-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-21
    • 2021-07-12
    相关资源
    最近更新 更多