tou*_*can 9 django django-subquery
我OuterRef在Django 1.11子查询中使用annotate()时遇到问题.示例模型:
class A(models.Model):
name = models.CharField(max_length=50)
class B(models.Model):
a = models.ForeignKey(A)
Run Code Online (Sandbox Code Playgroud)
现在是一个带有子查询的查询(这没有任何意义,但说明了我的问题):
A.objects.all().annotate(
s=Subquery(
B.objects.all().annotate(
n=OuterRef('name')
).values('n')[:1],
output_field=CharField()
)
)
Run Code Online (Sandbox Code Playgroud)
这会出现以下错误:
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'
Run Code Online (Sandbox Code Playgroud)
是否无法基于OuterRef注释子查询?
找到一个解决方法,这将允许我现在继续前进,但它并不好.
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, []
Run Code Online (Sandbox Code Playgroud)
更改OuterRef为使用自定义表达式
A.objects.all().annotate(
s=Subquery(
B.objects.all().annotate(
n=RawCol(A, 'name')
).values('n')[:1],
output_field=CharField()
)
)
Run Code Online (Sandbox Code Playgroud)
产量
SELECT "myapp_a"."id",
"myapp_a"."name",
(SELECT "myapp_a"."name" AS "n"
FROM "myapp_b" U0 LIMIT 1) AS "s"
FROM "myapp_a"
Run Code Online (Sandbox Code Playgroud)
这是 Django 中的一个已知错误,已在 3.0 中修复。
请参阅https://code.djangoproject.com/ticket/28621进行讨论。
如果您像我一样需要注释该字段以便可以在以下子查询中使用它,请记住您可以OuterRef像这样堆叠:
id__in=SubQuery(
MyModel.objects.filter(
field=OuterRef(OuterRef(some_outer_field))
)
)
Run Code Online (Sandbox Code Playgroud)
对于 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)
Run Code Online (Sandbox Code Playgroud)
(将其写为带有临时变量的两个命令更具可读性。这是类似于文档的风格。)
它被编译为一个 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
Run Code Online (Sandbox Code Playgroud)
(简化后没有“appname_”)
| 归档时间: |
|
| 查看次数: |
1926 次 |
| 最近记录: |