summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Wobrock <david.wobrock@gmail.com>2022-05-17 11:32:30 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-05-19 07:53:06 +0200
commit4a86883e0a7ed349d7ce3e91bde5e8e321effa1f (patch)
treeb20e904264cc7766c6d0be90fb482a509f0c5270
parentde9c08c0f3d79aba7fafbc5d8dea879d1fc5c5ca (diff)
downloaddjango-4a86883e0a7ed349d7ce3e91bde5e8e321effa1f.tar.gz
[4.0.x] Fixed #33705 -- Fixed crash when using IsNull() lookup in filters.
Thanks Florian Apolloner for the report. Thanks Simon Charette for the review. Backport of 9f5548952906c6ea97200c016734b4f519520a64 from main
-rw-r--r--django/db/models/lookups.py7
-rw-r--r--docs/releases/4.0.5.txt3
-rw-r--r--tests/lookup/tests.py13
3 files changed, 19 insertions, 4 deletions
diff --git a/django/db/models/lookups.py b/django/db/models/lookups.py
index d1b00bda97..9ee49f2af6 100644
--- a/django/db/models/lookups.py
+++ b/django/db/models/lookups.py
@@ -171,9 +171,10 @@ class Lookup(Expression):
c.lhs = self.lhs.resolve_expression(
query, allow_joins, reuse, summarize, for_save
)
- c.rhs = self.rhs.resolve_expression(
- query, allow_joins, reuse, summarize, for_save
- )
+ if hasattr(self.rhs, "resolve_expression"):
+ c.rhs = self.rhs.resolve_expression(
+ query, allow_joins, reuse, summarize, for_save
+ )
return c
def select_format(self, compiler, sql, params):
diff --git a/docs/releases/4.0.5.txt b/docs/releases/4.0.5.txt
index 8caf03bc5d..b626f9ef36 100644
--- a/docs/releases/4.0.5.txt
+++ b/docs/releases/4.0.5.txt
@@ -11,3 +11,6 @@ Bugfixes
* Fixed a bug in Django 4.0 where not all :setting:`OPTIONS <CACHES-OPTIONS>`
were passed to a Redis client (:ticket:`33681`).
+
+* Fixed a bug in Django 4.0 that caused a crash of ``QuerySet.filter()`` on
+ ``IsNull()`` expressions (:ticket:`33705`).
diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py
index 55c2907761..f65b6ad396 100644
--- a/tests/lookup/tests.py
+++ b/tests/lookup/tests.py
@@ -23,6 +23,7 @@ from django.db.models.lookups import (
Exact,
GreaterThan,
GreaterThanOrEqual,
+ IsNull,
LessThan,
LessThanOrEqual,
)
@@ -1243,7 +1244,7 @@ class LookupQueryingTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.s1 = Season.objects.create(year=1942, gt=1942)
- cls.s2 = Season.objects.create(year=1842, gt=1942)
+ cls.s2 = Season.objects.create(year=1842, gt=1942, nulled_text_field="text")
cls.s3 = Season.objects.create(year=2042, gt=1942)
def test_annotate(self):
@@ -1325,6 +1326,16 @@ class LookupQueryingTests(TestCase):
qs = Season.objects.filter(GreaterThan(F("year"), 1910))
self.assertCountEqual(qs, [self.s1, self.s3])
+ def test_isnull_lookup_in_filter(self):
+ self.assertSequenceEqual(
+ Season.objects.filter(IsNull(F("nulled_text_field"), False)),
+ [self.s2],
+ )
+ self.assertCountEqual(
+ Season.objects.filter(IsNull(F("nulled_text_field"), True)),
+ [self.s1, self.s3],
+ )
+
def test_filter_lookup_lhs(self):
qs = Season.objects.annotate(before_20=LessThan(F("year"), 2000)).filter(
before_20=LessThan(F("year"), 1900),