mirror of
https://github.com/django/django.git
synced 2025-01-03 06:55:47 +00:00
Fixed #26551 -- Fixed negated Q() queries that span relations.
Prevented queries from reusing trimmed joins.
This commit is contained in:
parent
ad36e5480d
commit
e124d2da94
1
AUTHORS
1
AUTHORS
@ -258,6 +258,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
flavio.curella@gmail.com
|
flavio.curella@gmail.com
|
||||||
Florian Apolloner <florian@apolloner.eu>
|
Florian Apolloner <florian@apolloner.eu>
|
||||||
Francisco Albarran Cristobal <pahko.xd@gmail.com>
|
Francisco Albarran Cristobal <pahko.xd@gmail.com>
|
||||||
|
François Freitag <mail@franek.fr>
|
||||||
Frank Tegtmeyer <fte@fte.to>
|
Frank Tegtmeyer <fte@fte.to>
|
||||||
Frank Wierzbicki
|
Frank Wierzbicki
|
||||||
Frank Wiles <frank@revsys.com>
|
Frank Wiles <frank@revsys.com>
|
||||||
|
@ -1192,10 +1192,12 @@ class Query:
|
|||||||
return self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]),
|
return self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]),
|
||||||
can_reuse, e.names_with_path)
|
can_reuse, e.names_with_path)
|
||||||
|
|
||||||
if can_reuse is not None:
|
# Update used_joins before trimming since they are reused to determine
|
||||||
can_reuse.update(join_list)
|
# which joins could be later promoted to INNER.
|
||||||
used_joins = set(used_joins).union(set(join_list))
|
used_joins = set(used_joins).union(set(join_list))
|
||||||
targets, alias, join_list = self.trim_joins(sources, join_list, path)
|
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:
|
if field.is_relation:
|
||||||
# No support for transforms for relational fields
|
# No support for transforms for relational fields
|
||||||
|
@ -648,8 +648,6 @@ class Employment(models.Model):
|
|||||||
title = models.CharField(max_length=128)
|
title = models.CharField(max_length=128)
|
||||||
|
|
||||||
|
|
||||||
# Bug #22429
|
|
||||||
|
|
||||||
class School(models.Model):
|
class School(models.Model):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -659,6 +657,8 @@ class Student(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Classroom(models.Model):
|
class Classroom(models.Model):
|
||||||
|
name = models.CharField(max_length=20)
|
||||||
|
has_blackboard = models.NullBooleanField()
|
||||||
school = models.ForeignKey(School, models.CASCADE)
|
school = models.ForeignKey(School, models.CASCADE)
|
||||||
students = models.ManyToManyField(Student, related_name='classroom')
|
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')
|
qs = Author.objects.filter(report__name='r4').filter(report__name='r1')
|
||||||
self.assertEqual(str(qs.query).count('JOIN'), 2)
|
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):
|
class DisjunctionPromotionTests(TestCase):
|
||||||
def test_disjunction_promotion_select_related(self):
|
def test_disjunction_promotion_select_related(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user