From bb6b9aa47279e78d2a17a571da1a1523e3b70c5c Mon Sep 17 00:00:00 2001 From: Karen Tracey Date: Wed, 10 Feb 2010 19:21:52 +0000 Subject: [PATCH] Fixed #11535: Corrected SQL generated for queries involving generic relations and ORed Q objects. Thanks to brianglass for report, tobias for fix and test, and Alex for review. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12405 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/sql/query.py | 4 +-- .../generic_relations_regress/models.py | 18 ++++++++++ .../generic_relations_regress/tests.py | 34 ++++++++++++++++++- 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 88b35719317..dde14946625 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1100,13 +1100,13 @@ class Query(object): for child in q_object.children: if connector == OR: refcounts_before = self.alias_refcount.copy() + self.where.start_subtree(connector) if isinstance(child, Node): - self.where.start_subtree(connector) self.add_q(child, used_aliases) - self.where.end_subtree() else: self.add_filter(child, connector, q_object.negated, can_reuse=used_aliases) + self.where.end_subtree() if connector == OR: # Aliases that were newly added or not used at all need to # be promoted to outer joins if they are nullable relations. diff --git a/tests/regressiontests/generic_relations_regress/models.py b/tests/regressiontests/generic_relations_regress/models.py index ab98a6706ee..d28385d398d 100644 --- a/tests/regressiontests/generic_relations_regress/models.py +++ b/tests/regressiontests/generic_relations_regress/models.py @@ -2,6 +2,10 @@ from django.db import models from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType +__all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', + 'CharLink', 'TextLink', 'OddRelation1', 'OddRelation2', + 'Contact', 'Organization', 'Note') + class Link(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() @@ -59,3 +63,17 @@ class OddRelation2(models.Model): name = models.CharField(max_length=100) tlinks = generic.GenericRelation(TextLink) +# models for test_q_object_or: +class Note(models.Model): + content_type = models.ForeignKey(ContentType) + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey() + note = models.TextField() + +class Contact(models.Model): + notes = generic.GenericRelation(Note) + +class Organization(models.Model): + name = models.CharField(max_length=255) + contacts = models.ManyToManyField(Contact, related_name='organizations') + diff --git a/tests/regressiontests/generic_relations_regress/tests.py b/tests/regressiontests/generic_relations_regress/tests.py index 6cecf80e779..45e86746e5b 100644 --- a/tests/regressiontests/generic_relations_regress/tests.py +++ b/tests/regressiontests/generic_relations_regress/tests.py @@ -1,6 +1,7 @@ from django.test import TestCase from django.contrib.contenttypes.models import ContentType -from models import Link, Place, Restaurant, Person, Address, CharLink, TextLink, OddRelation1, OddRelation2 +from django.db.models import Q +from models import * class GenericRelationTests(TestCase): @@ -40,3 +41,34 @@ class GenericRelationTests(TestCase): oddrel = OddRelation2.objects.create(name='tlink') tl = TextLink.objects.create(content_object=oddrel) oddrel.delete() + + def test_q_object_or(self): + """ + Tests that SQL query parameters for generic relations are properly + grouped when OR is used. + + Test for bug http://code.djangoproject.com/ticket/11535 + + In this bug the first query (below) works while the second, with the + query parameters the same but in reverse order, does not. + + The issue is that the generic relation conditions do not get properly + grouped in parentheses. + """ + note_contact = Contact.objects.create() + org_contact = Contact.objects.create() + note = Note.objects.create(note='note', content_object=note_contact) + org = Organization.objects.create(name='org name') + org.contacts.add(org_contact) + # search with a non-matching note and a matching org name + qs = Contact.objects.filter(Q(notes__note__icontains=r'other note') | + Q(organizations__name__icontains=r'org name')) + self.assertTrue(org_contact in qs) + # search again, with the same query parameters, in reverse order + qs = Contact.objects.filter( + Q(organizations__name__icontains=r'org name') | + Q(notes__note__icontains=r'other note')) + self.assertTrue(org_contact in qs) + + +