Skip to content

Commit

Permalink
[3.0.x] Fixed #31420 -- Fixed crash when filtering subquery annotatio…
Browse files Browse the repository at this point in the history
…n against a SimpleLazyObject.

Thanks Simon Charette for the solution and analysis.

Backport of 4237050 from master
  • Loading branch information
hramezani authored and felixxm committed Apr 6, 2020
1 parent 810f18c commit 22a2e97
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 4 deletions.
3 changes: 2 additions & 1 deletion django/db/models/sql/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ def __init__(self, model, where=WhereNode):
@property
def output_field(self):
if len(self.select) == 1:
return self.select[0].field
select = self.select[0]
return getattr(select, 'target', None) or select.field
elif len(self.annotation_select) == 1:
return next(iter(self.annotation_select.values())).output_field

Expand Down
4 changes: 3 additions & 1 deletion docs/releases/3.0.6.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ Django 3.0.6 fixes several bugs in 3.0.5.
Bugfixes
========

* ...
* Fixed a regression in Django 3.0 that caused a crash when filtering a
``Subquery()`` annotation of a queryset containing a single related field
against a ``SimpleLazyObject`` (:ticket:`31420`).
5 changes: 5 additions & 0 deletions tests/expressions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@
from django.db import models


class Manager(models.Model):
name = models.CharField(max_length=50)


class Employee(models.Model):
firstname = models.CharField(max_length=50)
lastname = models.CharField(max_length=50)
salary = models.IntegerField(blank=True, null=True)
manager = models.ForeignKey(Manager, models.CASCADE, null=True)

def __str__(self):
return '%s %s' % (self.firstname, self.lastname)
Expand Down
20 changes: 18 additions & 2 deletions tests/expressions/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
from django.db.models.sql.datastructures import Join
from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
from django.test.utils import Approximate, isolate_apps
from django.utils.functional import SimpleLazyObject

from .models import (
UUID, UUIDPK, Company, Employee, Experiment, Number, RemoteEmployee,
Result, SimulationRun, Time,
UUID, UUIDPK, Company, Employee, Experiment, Manager, Number,
RemoteEmployee, Result, SimulationRun, Time,
)


Expand Down Expand Up @@ -609,6 +610,21 @@ def test_subquery_filter_by_aggregate(self):
)
self.assertEqual(qs.get().float, 1.2)

def test_subquery_filter_by_lazy(self):
self.max.manager = Manager.objects.create(name='Manager')
self.max.save()
max_manager = SimpleLazyObject(
lambda: Manager.objects.get(pk=self.max.manager.pk)
)
qs = Company.objects.annotate(
ceo_manager=Subquery(
Employee.objects.filter(
lastname=OuterRef('ceo__lastname'),
).values('manager'),
),
).filter(ceo_manager=max_manager)
self.assertEqual(qs.get(), self.gmbh)

def test_aggregate_subquery_annotation(self):
with self.assertNumQueries(1) as ctx:
aggregate = Company.objects.annotate(
Expand Down

0 comments on commit 22a2e97

Please sign in to comment.