| 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()`. |