#37171 new Bug

Query that worked in Django 5.x now throws error: "The QuerySet value for an exact lookup must be limited to one result using slicing"

Reported by: Mark Baird Owned by:
Component: Database layer (models, ORM) Version: 6.0
Severity: Normal Keywords:
Cc: Mark Baird Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

We have a query that worked fine in Django 5.x but during our Django 6 upgrade we ran into failing tests because the query now throws an error. We were attempting to upgrade from Django 5.2.15 to Django 6.0.6. I've read through the change logs and I'm not seeing anything that seems to indicate that this is an expected change in Django 6.

I've attempted to create a minimal code example to demonstrate the issue. Given the following models:

class Bill(models.Model):
    amount = models.DecimalField(max_digits=17, decimal_places=2, blank=True, default=0)

class PaymentRequest(models.Model):
    bill = models.ForeignKey(Bill, on_delete=models.CASCADE, related_name="payment_requests")
    date_sent = models.DateField(blank=True, null=True)

class PartialPayment(models.Model):
    payment_request = models.ForeignKey(PaymentRequest, on_delete=models.CASCADE, related_name="partial_payments")
    amount = models.DecimalField(max_digits=17, decimal_places=2, blank=True, default=0)
    date_received = models.DateField(blank=True, null=True)

This test throws an exception in Django 6, while it worked fine in Django 5:

class BillPaymentsTests(TestCase):
    def test_query_unpaid_bills(self):
        bill = models.Bill(amount=100.00)
        payment_request = models.PaymentRequest(bill=bill)
        models.PartialPayment(payment_request=payment_request, amount=50.00)
        models.PartialPayment(payment_request=payment_request, amount=50.00)

        subquery = (models.PaymentRequest.objects
                    .filter(bill=OuterRef("pk")).values("bill")
                    .annotate(total_paid=Sum("partial_payments__amount"))
                    .values("total_paid"))
        unpaid_bills = models.Bill.objects.annotate(total_paid=Subquery(subquery)).exclude(amount=F("total_paid"))

        self.assertNotIn(bill, unpaid_bills)

The exception thrown is:

ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing.

On the line:

unpaid_bills = models.Bill.objects.annotate(total_paid=Subquery(subquery)).exclude(amount=F("total_paid"))

I recognize that I could simplify this code to remove the subquery, but please understand this is just a minimal example to demonstrate what appears to be a regression bug in Django 6. The actual code in my application is much more complicated and the subquery is needed.


I did discover a work-around, by simply swapping the fields in the exclude() condition, like so:

unpaid_bills = models.Bill.objects.annotate(total_paid=Subquery(subquery)).exclude(total_paid=F("amount"))

So we were able to get past this issue and complete our Django 6 upgrade, but I wanted to log this bug so the Django team is aware of the issue.

Change History (0)

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