1 | | While fixing #36404, I spotted the same mistake had been made for the newly added `Aggregate.order_by` from #35444. |
| 1 | While fixing #36404, I spotted the same mistake had been made for `Aggregate.order_by` (on `main`, from #35444), which was previously the PostgreSQL-specific `OrderableAggMixin.order_by` (on 5.2). |
| 2 | |
| 3 | The issue is reproduced in [https://github.com/adamchainz/django-ticket-36408 this example project], with models: |
| 4 | |
| 5 | {{{#!python |
| 6 | from django.db import models |
| 7 | |
| 8 | |
| 9 | class Book(models.Model): |
| 10 | position = models.IntegerField() |
| 11 | |
| 12 | |
| 13 | class Chapter(models.Model): |
| 14 | book = models.ForeignKey(Book, on_delete=models.CASCADE) |
| 15 | }}} |
| 16 | |
| 17 | And `QuerySet`: |
| 18 | |
| 19 | {{{#!python |
| 20 | from django.contrib.postgres.aggregates import ArrayAgg |
| 21 | from django.db.models import OuterRef, Subquery |
| 22 | |
| 23 | from example.models import Book, Chapter |
| 24 | |
| 25 | Book.objects.annotate( |
| 26 | chapter_ids=Subquery( |
| 27 | Chapter.objects.annotate( |
| 28 | ids=ArrayAgg( |
| 29 | "id", |
| 30 | order_by=[OuterRef("position")], |
| 31 | ) |
| 32 | ).values("ids")[:1] |
| 33 | ) |
| 34 | ) |
| 35 | }}} |
| 36 | |
| 37 | This error occurs: |
| 38 | |
| 39 | {{{ |
| 40 | $ python t.py |
| 41 | Traceback (most recent call last): |
| 42 | File "/.../t.py", line 15, in <module> |
| 43 | Chapter.objects.annotate( |
| 44 | ~~~~~~~~~~~~~~~~~~~~~~~~^ |
| 45 | ids=ArrayAgg( |
| 46 | ^^^^^^^^^^^^^ |
| 47 | ...<2 lines>... |
| 48 | ) |
| 49 | ^ |
| 50 | ).values("ids")[:1] |
| 51 | ^ |
| 52 | File "/.../django/db/models/manager.py", line 87, in manager_method |
| 53 | return getattr(self.get_queryset(), name)(*args, **kwargs) |
| 54 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ |
| 55 | File "/.../django/db/models/query.py", line 1647, in annotate |
| 56 | return self._annotate(args, kwargs, select=True) |
| 57 | ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 58 | File "/.../django/db/models/query.py", line 1699, in _annotate |
| 59 | clone.query.add_annotation( |
| 60 | ~~~~~~~~~~~~~~~~~~~~~~~~~~^ |
| 61 | annotation, |
| 62 | ^^^^^^^^^^^ |
| 63 | alias, |
| 64 | ^^^^^^ |
| 65 | select=select, |
| 66 | ^^^^^^^^^^^^^^ |
| 67 | ) |
| 68 | ^ |
| 69 | File "/.../django/db/models/sql/query.py", line 1218, in add_annotation |
| 70 | annotation = annotation.resolve_expression(self, allow_joins=True, reuse=None) |
| 71 | File "/.../django/contrib/postgres/aggregates/mixins.py", line 33, in resolve_expression |
| 72 | return super().resolve_expression(*args, **kwargs) |
| 73 | ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^ |
| 74 | File "/.../django/db/models/aggregates.py", line 63, in resolve_expression |
| 75 | c = super().resolve_expression(query, allow_joins, reuse, summarize) |
| 76 | File "/.../django/db/models/expressions.py", line 300, in resolve_expression |
| 77 | expr.resolve_expression(query, allow_joins, reuse, summarize) |
| 78 | ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 79 | File "/.../django/db/models/expressions.py", line 300, in resolve_expression |
| 80 | expr.resolve_expression(query, allow_joins, reuse, summarize) |
| 81 | ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 82 | File "/.../django/db/models/expressions.py", line 941, in resolve_expression |
| 83 | col = super().resolve_expression(*args, **kwargs) |
| 84 | File "/.../django/db/models/expressions.py", line 902, in resolve_expression |
| 85 | return query.resolve_ref(self.name, allow_joins, reuse, summarize) |
| 86 | ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 87 | File "/.../django/db/models/sql/query.py", line 2049, in resolve_ref |
| 88 | join_info = self.setup_joins( |
| 89 | field_list, self.get_meta(), self.get_initial_alias(), can_reuse=reuse |
| 90 | ) |
| 91 | File "/.../django/db/models/sql/query.py", line 1900, in setup_joins |
| 92 | path, final_field, targets, rest = self.names_to_path( |
| 93 | ~~~~~~~~~~~~~~~~~~^ |
| 94 | names[:pivot], |
| 95 | ^^^^^^^^^^^^^^ |
| 96 | ...<2 lines>... |
| 97 | fail_on_missing=True, |
| 98 | ^^^^^^^^^^^^^^^^^^^^^ |
| 99 | ) |
| 100 | ^ |
| 101 | File "/.../django/db/models/sql/query.py", line 1805, in names_to_path |
| 102 | raise FieldError( |
| 103 | ...<2 lines>... |
| 104 | ) |
| 105 | django.core.exceptions.FieldError: Cannot resolve keyword 'position' into field. Choices are: book, book_id, id |
| 106 | }}} |
| 107 | |
| 108 | This error again bisects to e306687a3a5507d59365ba9bf545010e5fd4b2a8. The cause is similar: duplicate `OuterRef` resolution occurs due to a clause left in `OrderableAggMixin.resolve_expression()`. |