Commit ad08f077 authored by Karen Tracey's avatar Karen Tracey
Browse files

[1.1.X] Fixed #11535: Corrected SQL generated for queries involving generic...

[1.1.X] 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. 

r12405 from trunk.


git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12406 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 750c889a
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1665,13 +1665,13 @@ class BaseQuery(object):
            for child in q_object.children:
                if connector == OR:
                    refcounts_before = self.alias_refcount.copy()
                if isinstance(child, Node):
                self.where.start_subtree(connector)
                if isinstance(child, Node):
                    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.
+18 −0
Original line number Diff line number Diff line
@@ -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')
+33 −1
Original line number Diff line number Diff line
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)