Kaydet (Commit) e124d2da authored tarafından François Freitag's avatar François Freitag Kaydeden (comit) Tim Graham

Fixed #26551 -- Fixed negated Q() queries that span relations.

Prevented queries from reusing trimmed joins.
üst ad36e548
......@@ -258,6 +258,7 @@ answer newbie questions, and generally made Django that much better:
flavio.curella@gmail.com
Florian Apolloner <florian@apolloner.eu>
Francisco Albarran Cristobal <pahko.xd@gmail.com>
François Freitag <mail@franek.fr>
Frank Tegtmeyer <fte@fte.to>
Frank Wierzbicki
Frank Wiles <frank@revsys.com>
......
......@@ -1192,10 +1192,12 @@ class Query:
return self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]),
can_reuse, e.names_with_path)
if can_reuse is not None:
can_reuse.update(join_list)
# Update used_joins before trimming since they are reused to determine
# which joins could be later promoted to INNER.
used_joins = set(used_joins).union(set(join_list))
targets, alias, join_list = self.trim_joins(sources, join_list, path)
if can_reuse is not None:
can_reuse.update(join_list)
if field.is_relation:
# No support for transforms for relational fields
......
......@@ -648,8 +648,6 @@ class Employment(models.Model):
title = models.CharField(max_length=128)
# Bug #22429
class School(models.Model):
pass
......@@ -659,6 +657,8 @@ class Student(models.Model):
class Classroom(models.Model):
name = models.CharField(max_length=20)
has_blackboard = models.NullBooleanField()
school = models.ForeignKey(School, models.CASCADE)
students = models.ManyToManyField(Student, related_name='classroom')
......
......@@ -3147,6 +3147,25 @@ class JoinReuseTest(TestCase):
qs = Author.objects.filter(report__name='r4').filter(report__name='r1')
self.assertEqual(str(qs.query).count('JOIN'), 2)
def test_inverted_q_across_relations(self):
"""
When a trimmable join is specified in the query (here school__), the
ORM detects it and removes unnecessary joins. The set of reusable joins
are updated after trimming the query so that other lookups don't
consider that the outer query's filters are in effect for the subquery
(#26551).
"""
springfield_elementary = School.objects.create()
hogward = School.objects.create()
Student.objects.create(school=springfield_elementary)
hp = Student.objects.create(school=hogward)
Classroom.objects.create(school=hogward, name='Potion')
Classroom.objects.create(school=springfield_elementary, name='Main')
qs = Student.objects.filter(
~(Q(school__classroom__name='Main') & Q(school__classroom__has_blackboard=None))
)
self.assertSequenceEqual(qs, [hp])
class DisjunctionPromotionTests(TestCase):
def test_disjunction_promotion_select_related(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment