15

OuterRef在 Django 1.11 子查询中使用 annotate() 时遇到问题。示例模型:

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


class B(models.Model):
    a = models.ForeignKey(A)

现在一个带有子查询的查询(这实际上没有任何意义,但说明了我的问题):

A.objects.all().annotate(
    s=Subquery(
        B.objects.all().annotate(
            n=OuterRef('name')
        ).values('n')[:1],
        output_field=CharField()
    )
)

这给出了以下错误:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "myapp/models.py", line 25, in a
    n=OuterRef('name')
  File ".virtualenv/lib/python2.7/site-packages/django/db/models/query.py", line 948, in annotate
    if alias in annotations and annotation.contains_aggregate:
AttributeError: 'ResolvedOuterRef' object has no attribute 'contains_aggregate'

不能根据 OuterRef 注释子查询吗?


更新#1

找到了一个解决方法,可以让我现在继续前进,但这并不好。

class RawCol(Expression):

    def __init__(self, model, field_name, output_field=None):
        field = model._meta.get_field(field_name)
        self.table = model._meta.db_table
        self.column = field.column
        super().__init__(output_field=output_field)

    def as_sql(self, compiler, connection):
        sql = f'"{self.table}"."{self.column}"'
        return sql, []

更改OuterRef为使用自定义表达式

A.objects.all().annotate(
    s=Subquery(
        B.objects.all().annotate(
            n=RawCol(A, 'name')
        ).values('n')[:1],
        output_field=CharField()
    )
)

产量

SELECT "myapp_a"."id",
       "myapp_a"."name",

  (SELECT "myapp_a"."name" AS "n"
   FROM "myapp_b" U0 LIMIT 1) AS "s"
FROM "myapp_a"
4

2 回答 2

3

这是 Django 中的一个已知错误,已在 3.0 中修复。

有关讨论,请参阅https://code.djangoproject.com/ticket/28621

如果您像我一样需要注释该字段以便可以在以下子查询中使用它,请记住您可以OuterRef像这样堆叠:


id__in=SubQuery(
    MyModel.objects.filter(
        field=OuterRef(OuterRef(some_outer_field))
    )
)
于 2020-04-15T10:38:45.333 回答
2

对于 A 的每一行,B 的一个相关行的一个字段都可以通过这种方式进行注释。

subq = Subquery(B.objects.filter(a=OuterRef('pk')).order_by().values('any_field_of_b')[:1])
qs = A.objects.all().annotate(b_field=subq)

(将它写成两个带有临时变量的命令更具可读性。这是一种类似于 docs 的风格。)

它被编译为一个 SQL 请求:

>>> print(str(qs.suery))
SELECT a.id, a.name,
  (SELECT U0.any_field_of_b FROM b U0 WHERE U0.a_id = (a.id)  LIMIT 1) AS b_field
FROM a

(没有“appname_”的简化)

于 2019-04-02T20:41:12.473 回答