【问题标题】:django's .extra(where= clauses are clobbered by table-renaming .filter(foo__in=... subselectsdjango 的 .extra(where= 子句被表重命名 .filter(foo__in=... subselects
【发布时间】:2010-07-20 20:00:43
【问题描述】:

简而言之,过滤器内的所有查询的表名都重命名为 u0、u1、...,所以我的额外 where 子句不知道要指向哪个表。我希望不必为我可能对这些数据进行子选择的每一种方式手动进行所有查询,而我目前的解决方法是将我的额外查询转换为 pk values_lists,但这些查询真的很慢而且令人厌恶。

这就是这一切的样子。除了指向 products_product.id 的第一条 sql 行之外,您几乎可以忽略此管理器方法的额外内容的详细信息:

def by_status(self, *statii):
    return self.extra(where=["""products_product.id IN                                                                                                                                                  
        (SELECT recent.product_id                                                                                                                                                                          
          FROM (                                                                                                                                                                                           
            SELECT product_id, MAX(start_date) AS latest                                                                                                                                                   
            FROM products_productstatus                                                                                                                                                                    
            GROUP BY product_id                                                                                                                                                                            
          ) AS recent                                                                                                                                                                                      
          JOIN products_productstatus AS ps ON ps.product_id = recent.product_id                                                                                                                           
          WHERE ps.start_date = recent.latest                                                                                                                                                              
            AND ps.status IN (%s))""" % (', '.join([str(stat) for stat in statii]),)])

这对于仅涉及 products_product 表的所有情况都非常有效。

当我想要这些产品作为子选择时,我会这样做:

Piece.objects.filter(
    product__in=Product.objects.filter(
        pk__in=list(
            Product.objects.by_status(FEATURED).values_list('id', flat=True))))

我怎样才能保持查询集的泛化能力,但仍然使用额外的 where 子句?

【问题讨论】:

  • 如果不是很明显,我希望看到该解决方案的“Product.objects.filter(pk__in=list(”部分消失。
  • 能否请您发布您想在应用程序中使用的实际代码以及 django 将生成的查询(即使它的 SQL 表命名错误)。您可以使用print Piece.objects.filter(...).query 执行此操作。谢谢!
  • Piece.objects.filter(product__in=Product.objects.by_status(FEATURED))
  • 我想我想要的是让我的额外位置以某种方式被告知其表名正在更改,并让它适当地适应。

标签: django django-orm subquery


【解决方案1】:

起初:这个问题对我来说并不完全清楚。您问题中的第二个代码块是您要执行的实际代码吗?如果是这种情况,查询应该按预期工作,因为没有执行子选择。

我假设您想使用第二个代码块,而子选择周围没有 list(),以防止执行第二个查询。


django 文档提到了这个问题in the documentation about the extra method。然而,要克服这个问题并不容易。

最简单但最“hakish”的解决方案是观察 django 为您要在 extra 方法中查询的表生成了哪个表别名。只要始终以相同的方式构造查询,就可以依赖此别名的持久命名(您不会更改导致连接的多个 extra 方法或 filter 调用的顺序)。

您可以使用以下命令检查将在 DB 查询集中执行的查询:

print Model.objects.filter(...).query

这将显示用于您要查询的表的别名。

【讨论】:

  • 对,我知道我可以根据具体情况重写额外的使用 u0 或有时 u1 的位置,但我希望能够为我的模板/视图人员提供一个正常的查询集就像我在其他地方给他们的一样。
【解决方案2】:

从 Django 1.11 开始,您应该能够使用 SubqueryOuterRef 生成与您的 extra 等效的查询(使用相关子查询而不是连接):

def by_status(self, *statii):
    return self.filter(
        id__in=Subquery(ProductStatus.values("product_id").filter(
            status__in=statii,
            product__in=Subquery(ProductStatus.objects.values(
                "product_id",
            ).annotate(
                latest=Max("start_date"),
            ).filter(
                latest=OuterRef("start_date"),
            ).values("product_id"),
        ),
    )

您也可以使用 Window 表达式来做到这一点(从 Django 2.0 开始)。

请注意,这是未经测试的,因此可能需要一些调整。

【讨论】:

    猜你喜欢
    • 2015-10-06
    • 1970-01-01
    • 2017-02-10
    • 2017-02-20
    • 1970-01-01
    • 2015-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多