summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHasan Ramezani <hasan.r67@gmail.com>2020-04-04 20:55:20 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-04-06 07:11:29 +0200
commit4237050684427db45ea834fe89d9e11c0520201e (patch)
tree111e56066d6f098306e9c12bbd3bbf5ec55b05c4
parentfa5e7e46d875d4143510944f19d79df7b1739bab (diff)
Fixed #31420 -- Fixed crash when filtering subquery annotation against a SimpleLazyObject.
Thanks Simon Charette for the solution and analysis.
-rw-r--r--django/db/models/sql/query.py3
-rw-r--r--docs/releases/3.0.6.txt4
-rw-r--r--tests/expressions/models.py5
-rw-r--r--tests/expressions/tests.py20
4 files changed, 28 insertions, 4 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 9fe0c9a656..e5524a8198 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -233,7 +233,8 @@ class Query(BaseExpression):
@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
diff --git a/docs/releases/3.0.6.txt b/docs/releases/3.0.6.txt
index e25d6e8fc8..6fd7aad02b 100644
--- a/docs/releases/3.0.6.txt
+++ b/docs/releases/3.0.6.txt
@@ -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`).
diff --git a/tests/expressions/models.py b/tests/expressions/models.py
index a81ba9e16b..a3129207fa 100644
--- a/tests/expressions/models.py
+++ b/tests/expressions/models.py
@@ -6,10 +6,15 @@ import uuid
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)
diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py
index 0fac4642f7..b17a286d5d 100644
--- a/tests/expressions/tests.py
+++ b/tests/expressions/tests.py
@@ -21,10 +21,11 @@ from django.db.models.sql import constants
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,
)
@@ -608,6 +609,21 @@ class BasicExpressionsTests(TestCase):
)
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(