Ticket #11535: generic_relations_or.diff

File generic_relations_or.diff, 5.1 KB (added by Tobias McNulty, 14 years ago)

patch with test demonstrating problem and proposed fix

  • django/db/models/sql/query.py

    diff -r d0dc9aaff6af django/db/models/sql/query.py
    a b  
    16701670            for child in q_object.children:
    16711671                if connector == OR:
    16721672                    refcounts_before = self.alias_refcount.copy()
     1673                # a subtree may also be necesary for add_filter, in the
     1674                # case where extra_filters is a non-empty list
     1675                self.where.start_subtree(connector)
    16731676                if isinstance(child, Node):
    1674                     self.where.start_subtree(connector)
    16751677                    self.add_q(child, used_aliases)
    1676                     self.where.end_subtree()
    16771678                else:
    16781679                    self.add_filter(child, connector, q_object.negated,
    16791680                            can_reuse=used_aliases)
     1681                self.where.end_subtree()
    16801682                if connector == OR:
    16811683                    # Aliases that were newly added or not used at all need to
    16821684                    # be promoted to outer joins if they are nullable relations.
  • tests/regressiontests/generic_relations_regress/models.py

    diff -r d0dc9aaff6af tests/regressiontests/generic_relations_regress/models.py
    a b  
    22from django.contrib.contenttypes import generic
    33from django.contrib.contenttypes.models import ContentType
    44
     5__all__ = ('Link', 'Place', 'Restaurant', 'Contact', 'Organization', 'Note')
     6
     7
    58class Link(models.Model):
     9
    610    content_type = models.ForeignKey(ContentType)
    711    object_id = models.PositiveIntegerField()
    812    content_object = generic.GenericForeignKey()
     
    1014    def __unicode__(self):
    1115        return "Link to %s id=%s" % (self.content_type, self.object_id)
    1216
     17
    1318class Place(models.Model):
     19
    1420    name = models.CharField(max_length=100)
    1521    links = generic.GenericRelation(Link)
    16    
     22
    1723    def __unicode__(self):
    1824        return "Place: %s" % self.name
    19    
    20 class Restaurant(Place):
     25
     26
     27class Restaurant(Place):
     28
    2129    def __unicode__(self):
    22         return "Restaurant: %s" % self.name
    23  No newline at end of file
     30        return "Restaurant: %s" % self.name
     31
     32
     33# models for test_q_object_or:
     34class Note(models.Model):
     35
     36    content_type = models.ForeignKey(ContentType)
     37    object_id = models.PositiveIntegerField()
     38    content_object = generic.GenericForeignKey()
     39    note = models.TextField()
     40
     41
     42class Contact(models.Model):
     43
     44    notes = generic.GenericRelation(Note)
     45
     46
     47class Organization(models.Model):
     48
     49    name = models.CharField(max_length=255)
     50    contacts = models.ManyToManyField(Contact, related_name='organizations')
  • tests/regressiontests/generic_relations_regress/tests.py

    diff -r d0dc9aaff6af tests/regressiontests/generic_relations_regress/tests.py
    a b  
    11from django.test import TestCase
    22from django.contrib.contenttypes.models import ContentType
    3 from models import Link, Place, Restaurant
     3from models import *
     4from django.db.models import Q
     5
    46
    57class GenericRelationTests(TestCase):
    6    
     8
    79    def test_inherited_models_content_type(self):
    810        """
    911        Test that GenericRelations on inherited classes use the correct content
    1012        type.
    1113        """
    12        
     14
    1315        p = Place.objects.create(name="South Park")
    14         r = Restaurant.objects.create(name="Chubby's")       
     16        r = Restaurant.objects.create(name="Chubby's")
    1517        l1 = Link.objects.create(content_object=p)
    1618        l2 = Link.objects.create(content_object=r)
    1719        self.assertEqual(list(p.links.all()), [l1])
    1820        self.assertEqual(list(r.links.all()), [l2])
    19        
    20  No newline at end of file
     21
     22    def test_q_object_or(self):
     23        """
     24        Tests that SQL query parameters for generic relations are properly
     25        grouped when OR is used.
     26
     27        Test for bug http://code.djangoproject.com/ticket/11535
     28
     29        In this bug the first query (below) works while the second, with the
     30        query parameters the same but in reverse order, does not.
     31
     32        The issue is that the generic relation conditions do not get properly
     33        grouped in parentheses.
     34        """
     35        note_contact = Contact.objects.create()
     36        org_contact = Contact.objects.create()
     37        note = Note.objects.create(note='note', content_object=note_contact)
     38        org = Organization.objects.create(name='org name')
     39        org.contacts.add(org_contact)
     40        # search with a non-matching note and a matching org name
     41        qs = Contact.objects.filter(Q(notes__note__icontains=r'other note') |
     42                            Q(organizations__name__icontains=r'org name'))
     43        self.assertTrue(org_contact in qs)
     44        # search again, with the same query parameters, in reverse order
     45        qs = Contact.objects.filter(
     46            Q(organizations__name__icontains=r'org name') |
     47            Q(notes__note__icontains=r'other note'))
     48        self.assertTrue(org_contact in qs)
Back to Top