Opened 6 years ago

Closed 6 years ago

Last modified 6 years ago

#24420 closed Bug (fixed)

Can't order_by annotated field if annotated field uses conditional expression

Reported by: Kris Fields Owned by: Josh Smeaton
Component: Database layer (models, ORM) Version: 1.8beta1
Severity: Normal Keywords: order_by, annotation, conditional expression
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Continuing with the Client example on https://docs.djangoproject.com/en/dev/ref/models/conditional-expressions/

from django.db import models

class Client(models.Model):
    REGULAR = 'R'
    GOLD = 'G'
    PLATINUM = 'P'
    ACCOUNT_TYPE_CHOICES = (
        (REGULAR, 'Regular'),
        (GOLD, 'Gold'),
        (PLATINUM, 'Platinum'),
    )
    name = models.CharField(max_length=50)
    registered_on = models.DateField()
    account_type = models.CharField(
        max_length=1,
        choices=ACCOUNT_TYPE_CHOICES,
        default=REGULAR,
    )

Client.objects.annotate(
    discount=Case(
        When(account_type=Client.GOLD, then=Value('5%')),
        When(account_type=Client.PLATINUM, then=Value('10%')),
        default=Value('0%'),
        output_field=CharField(),
        ),
    ).order_by('discount')

Error is:

<repr(<django.db.models.query.QuerySet at 0x10afaef10>) failed: AttributeError: 'WhereNode' object has no attribute 'resolve_expression'>

Change History (5)

comment:1 Changed 6 years ago by Josh Smeaton

Owner: changed from nobody to Josh Smeaton
Status: newassigned
Triage Stage: UnreviewedAccepted

comment:2 Changed 6 years ago by Josh Smeaton

See: https://github.com/django/django/pull/4239

Funnily enough, being explicit worked before this patch:

CaseTestModel.objects.filter(integer__lte=2).annotate(test=Case(
                When(integer=1, then=2),
                When(integer=2, then=1),
                default=3,
                output_field=models.IntegerField(),
            )).order_by(F('test').asc())

The problem is that the same instance of each When clause has resolve_expression called on it multiple times. Once for the annotation, and once again for the order_by. The problem is that When.resolve_expression converts the Q() into a WhereNode(), and overwrites the condition property. It's not idempotent.

Last edited 6 years ago by Josh Smeaton (previous) (diff)

comment:3 Changed 6 years ago by Tim Graham

Component: UncategorizedDatabase layer (models, ORM)
Has patch: set
Triage Stage: AcceptedReady for checkin

Seems like a candidate for backport.

comment:4 Changed 6 years ago by Josh Smeaton <josh.smeaton@…>

Resolution: fixed
Status: assignedclosed

In ceaf31adfff3801f1092a215f73704e15a70e90c:

Fixed #24420 -- Allowed ordering by case expressions

comment:5 Changed 6 years ago by Josh Smeaton <josh.smeaton@…>

In 823f8cdbc91f85a8ab3cb1ccfec5659037b5c148:

[1.8.x] Fixed #24420 -- Allowed ordering by case expressions

Backport of ceaf31adfff3801f1092a215f73704e15a70e90c from master

Note: See TracTickets for help on using tickets.
Back to Top