diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 1ca56d60f1..b70a15755b 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -347,7 +347,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/docs/releases/1.8.1.txt b/docs/releases/1.8.1.txt index 976179c41e..15b7546933 100644 --- a/docs/releases/1.8.1.txt +++ b/docs/releases/1.8.1.txt @@ -53,3 +53,5 @@ Bugfixes * Fixed queries where an expression was referenced in ``order_by()``, but wasn't part of the select clause. An example query is ``qs.annotate(foo=F('field')).values('pk').order_by('foo'))`` (:ticket:`24615`). + +* Fixed a database table name quoting regression (:ticket:`24605`). diff --git a/tests/queries/models.py b/tests/queries/models.py index bc540756fb..5d63ae9ea8 100644 --- a/tests/queries/models.py +++ b/tests/queries/models.py @@ -713,3 +713,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() + + 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 f688d0ed90..b4ea646fde 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -21,16 +21,16 @@ from .models import ( FK1, X, Annotation, Article, Author, BaseA, Book, CategoryItem, CategoryRelationship, Celebrity, Channel, Chapter, Child, ChildObjectA, Classroom, Company, Cover, CustomPk, CustomPkTag, Detail, DumbCategory, - Eaten, Employment, ExtraInfo, Fan, Food, Identifier, Item, Job, + 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, ProxyObjectA, ProxyObjectB, Ranking, Related, RelatedObject, - Report, ReservedName, Responsibility, School, SharedConnection, - SimpleCategory, SingleObject, SpecialCategory, Staff, StaffUser, Student, - Tag, Task, Ticket21203Child, Ticket21203Parent, Ticket23605A, Ticket23605B, - Ticket23605C, TvChef, Valid, + ProxyCategory, ProxyObjectA, ProxyObjectB, Ranking, Related, + RelatedIndividual, RelatedObject, Report, ReservedName, Responsibility, + School, SharedConnection, SimpleCategory, SingleObject, SpecialCategory, + Staff, StaffUser, Student, Tag, Task, Ticket21203Child, Ticket21203Parent, + Ticket23605A, Ticket23605B, Ticket23605C, TvChef, Valid, ) @@ -3690,3 +3690,27 @@ class TestInvalidValuesRelation(TestCase): Annotation.objects.filter(tag='abc') with self.assertRaises(ValueError): Annotation.objects.filter(tag__in=[123, 'abc']) + + +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 + )