From c3a9820251ab06bbe491f11f3112031fb2dad8bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Thu, 16 Apr 2015 08:39:31 +0300 Subject: [PATCH] [1.7.x] Fixed #24605 -- Fixed incorrect reference to alias in subquery. Thanks to charettes and priidukull for investigating the issue, and to kurevin for the report. Backport of 355c5edd9390caad5725375abca03460805f663b from master --- django/db/models/sql/compiler.py | 3 +- docs/releases/1.7.8.txt | 8 +++-- tests/queries/models.py | 15 ++++++++ tests/queries/tests.py | 62 ++++++++++++++++++++++---------- 4 files changed, 67 insertions(+), 21 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 90ac90f9ae..e938253145 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -56,7 +56,8 @@ class SQLCompiler(object): if name in self.quote_cache: return self.quote_cache[name] if ((name in self.query.alias_map and name not in self.query.table_map) or - name in self.query.extra_select or name in self.query.external_aliases): + name in self.query.extra_select or ( + name in self.query.external_aliases and name not in self.query.table_map)): self.quote_cache[name] = name return name r = self.connection.ops.quote_name(name) diff --git a/docs/releases/1.7.8.txt b/docs/releases/1.7.8.txt index 0e5255048a..912171f8da 100644 --- a/docs/releases/1.7.8.txt +++ b/docs/releases/1.7.8.txt @@ -4,5 +4,9 @@ Django 1.7.8 release notes *Under development* -Django 1.7.8 fixes database introspection with SQLite 3.8.9 (released April 8, -2015) (:ticket:`24637`). +Django 1.7.8 fixes: + +* Database introspection with SQLite 3.8.9 (released April 8, 2015) + (:ticket:`24637`). + +* A database table name quoting regression in 1.7.2 (:ticket:`24605`). diff --git a/tests/queries/models.py b/tests/queries/models.py index 6e6a0ce5e1..e86793dafd 100644 --- a/tests/queries/models.py +++ b/tests/queries/models.py @@ -690,3 +690,18 @@ class Ticket23605B(models.Model): class Ticket23605C(models.Model): field_c0 = models.FloatField() + + +# db_table names have capital letters to ensure they are quoted in queries. +class Individual(models.Model): + alive = models.BooleanField(default=False) + + class Meta: + db_table = 'Individual' + + +class RelatedIndividual(models.Model): + related = models.ForeignKey(Individual, related_name='related_individual') + + class Meta: + db_table = 'RelatedIndividual' diff --git a/tests/queries/tests.py b/tests/queries/tests.py index 1c3dd2adda..47eb3c1839 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -1,35 +1,37 @@ from __future__ import unicode_literals -from collections import OrderedDict import datetime -from operator import attrgetter import pickle import unittest import warnings +from collections import OrderedDict +from operator import attrgetter from django.core.exceptions import FieldError -from django.db import connection, DEFAULT_DB_ALIAS -from django.db.models import Count, F, Q -from django.db.models.sql.where import WhereNode, EverythingNode, NothingNode +from django.db import DEFAULT_DB_ALIAS, connection +from django.db.models import F, Q, Count from django.db.models.sql.datastructures import EmptyResultSet +from django.db.models.sql.where import EverythingNode, NothingNode, WhereNode from django.test import TestCase, skipUnlessDBFeature -from django.test.utils import str_prefix, CaptureQueriesContext +from django.test.utils import CaptureQueriesContext, str_prefix from django.utils import six from django.utils.six.moves import range from .models import ( - Annotation, Article, Author, Celebrity, Child, Cover, Detail, DumbCategory, - ExtraInfo, Fan, Item, LeafA, Join, LeafB, LoopX, LoopZ, ManagedModel, - Member, NamedCategory, Note, Number, Plaything, PointerA, Ranking, Related, - Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Node, ObjectA, - ObjectB, ObjectC, CategoryItem, SimpleCategory, SpecialCategory, - OneToOneCategory, NullableName, ProxyCategory, SingleObject, RelatedObject, - ModelA, ModelB, ModelC, ModelD, Responsibility, Job, JobResponsibilities, - BaseA, FK1, Identifier, Program, Channel, Page, Paragraph, Chapter, Book, - MyObject, Order, OrderItem, SharedConnection, Task, Staff, StaffUser, - CategoryRelationship, Ticket21203Parent, Ticket21203Child, Person, - Company, Employment, CustomPk, CustomPkTag, Classroom, School, Student, - Ticket23605A, Ticket23605B, Ticket23605C) + FK1, X, Annotation, Article, Author, BaseA, Book, CategoryItem, + CategoryRelationship, Celebrity, Channel, Chapter, Child, Classroom, + Company, Cover, CustomPk, CustomPkTag, Detail, DumbCategory, Eaten, + Employment, ExtraInfo, Fan, Food, Identifier, Individual, Item, Job, + JobResponsibilities, Join, LeafA, LeafB, LoopX, LoopZ, ManagedModel, + Member, ModelA, ModelB, ModelC, ModelD, MyObject, NamedCategory, Node, + Note, NullableName, Number, ObjectA, ObjectB, ObjectC, OneToOneCategory, + Order, OrderItem, Page, Paragraph, Person, Plaything, PointerA, Program, + ProxyCategory, Ranking, Related, RelatedIndividual, RelatedObject, Report, + ReservedName, Responsibility, School, SharedConnection, SimpleCategory, + SingleObject, SpecialCategory, Staff, StaffUser, Student, Tag, Task, + Ticket21203Child, Ticket21203Parent, Ticket23605A, Ticket23605B, + Ticket23605C, TvChef, Valid, +) class BaseQuerysetTest(TestCase): @@ -3448,3 +3450,27 @@ class Ticket23605Tests(TestCase): self.assertQuerysetEqual(qs1, [a1], lambda x: x) qs2 = Ticket23605A.objects.exclude(complex_q) self.assertQuerysetEqual(qs2, [a2], lambda x: x) + + +class TestTicket24605(TestCase): + def test_ticket_24605(self): + """ + Subquery table names should be quoted. + """ + i1 = Individual.objects.create(alive=True) + RelatedIndividual.objects.create(related=i1) + i2 = Individual.objects.create(alive=False) + RelatedIndividual.objects.create(related=i2) + i3 = Individual.objects.create(alive=True) + i4 = Individual.objects.create(alive=False) + + self.assertQuerysetEqual( + Individual.objects.filter(Q(alive=False), Q(related_individual__isnull=True)), + [i4], lambda x: x + ) + self.assertQuerysetEqual( + Individual.objects.exclude( + Q(alive=False), Q(related_individual__isnull=True) + ).order_by('pk'), + [i1, i2, i3], lambda x: x + )