summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--django/db/models/sql/query.py2
-rw-r--r--docs/releases/1.11.5.txt3
-rw-r--r--tests/queries/models.py5
-rw-r--r--tests/queries/tests.py18
5 files changed, 25 insertions, 4 deletions
diff --git a/AUTHORS b/AUTHORS
index d1280eb6c9..1479bb8786 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -110,6 +110,7 @@ answer newbie questions, and generally made Django that much better:
berto
Bill Fenner <fenner@gmail.com>
Bjørn Stabell <bjorn@exoweb.net>
+ Bo Marchman <bo.marchman@gmail.com>
Bojan Mihelac <bmihelac@mihelac.org>
Bouke Haarsma <bouke@haarsma.eu>
Božidar Benko <bbenko@gmail.com>
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index d1c4e5446b..e51b1037ca 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -133,7 +133,7 @@ class Query(object):
# types they are. The key is the alias of the joined table (possibly
# the table name) and the value is a Join-like object (see
# sql.datastructures.Join for more information).
- self.alias_map = {}
+ self.alias_map = OrderedDict()
# Sometimes the query contains references to aliases in outer queries (as
# a result of split_exclude). Correct alias quoting needs to know these
# aliases too.
diff --git a/docs/releases/1.11.5.txt b/docs/releases/1.11.5.txt
index cb9e466248..139371d90e 100644
--- a/docs/releases/1.11.5.txt
+++ b/docs/releases/1.11.5.txt
@@ -40,3 +40,6 @@ Bugfixes
weren't logged in the admin change history (:ticket:`27998`) and prevented
``ManyToManyField`` initial data in model forms from being affected by
subsequent model changes (:ticket:`28543`).
+
+* Fixed non-deterministic results or an ``AssertionError`` crash in some
+ queries with multiple joins (:ticket:`26522`).
diff --git a/tests/queries/models.py b/tests/queries/models.py
index ab03b9c248..fd76623c33 100644
--- a/tests/queries/models.py
+++ b/tests/queries/models.py
@@ -709,6 +709,11 @@ class Classroom(models.Model):
students = models.ManyToManyField(Student, related_name='classroom')
+class Teacher(models.Model):
+ schools = models.ManyToManyField(School)
+ friends = models.ManyToManyField('self')
+
+
class Ticket23605AParent(models.Model):
pass
diff --git a/tests/queries/tests.py b/tests/queries/tests.py
index 6633985f81..877cf8091e 100644
--- a/tests/queries/tests.py
+++ b/tests/queries/tests.py
@@ -28,9 +28,9 @@ from .models import (
ProxyCategory, ProxyObjectA, ProxyObjectB, Ranking, Related,
RelatedIndividual, RelatedObject, Report, ReportComment, ReservedName,
Responsibility, School, SharedConnection, SimpleCategory, SingleObject,
- SpecialCategory, Staff, StaffUser, Student, Tag, Task, Ticket21203Child,
- Ticket21203Parent, Ticket23605A, Ticket23605B, Ticket23605C, TvChef, Valid,
- X,
+ SpecialCategory, Staff, StaffUser, Student, Tag, Task, Teacher,
+ Ticket21203Child, Ticket21203Parent, Ticket23605A, Ticket23605B,
+ Ticket23605C, TvChef, Valid, X,
)
@@ -1391,6 +1391,18 @@ class Queries4Tests(TestCase):
self.assertEqual(len(combined), 1)
self.assertEqual(combined[0].name, 'a1')
+ def test_join_reuse_order(self):
+ # Join aliases are reused in order. This shouldn't raise AssertionError
+ # because change_map contains a circular reference (#26522).
+ s1 = School.objects.create()
+ s2 = School.objects.create()
+ s3 = School.objects.create()
+ t1 = Teacher.objects.create()
+ otherteachers = Teacher.objects.exclude(pk=t1.pk).exclude(friends=t1)
+ qs1 = otherteachers.filter(schools=s1).filter(schools=s2)
+ qs2 = otherteachers.filter(schools=s1).filter(schools=s3)
+ self.assertQuerysetEqual(qs1 | qs2, [])
+
def test_ticket7095(self):
# Updates that are filtered on the model being updated are somewhat
# tricky in MySQL.