Ticket #12252: 12252a.diff

File 12252a.diff, 5.8 KB (added by benreynwar, 5 years ago)
  • django/db/models/sql/query.py

     
    407407            "Cannot combine a unique query with a non-unique query."
    408408
    409409        self.remove_inherited_models()
     410        l_tables = set([a for a in self.tables if self.alias_refcount[a]])
     411        r_tables = set([a for a in rhs.tables if rhs.alias_refcount[a]])
    410412        # Work out how to relabel the rhs aliases, if necessary.
    411413        change_map = {}
    412414        used = set()
     
    424426            first = False
    425427
    426428        # So that we don't exclude valid results in an "or" query combination,
    427         # the first join that is exclusive to the lhs (self) must be converted
     429        # all joins exclusive to either the lhs or the rhs must be converted
    428430        # to an outer join.
    429431        if not conjunction:
    430             for alias in self.tables[1:]:
    431                 if self.alias_refcount[alias] == 1:
    432                     self.promote_alias(alias, True)
    433                     break
     432            # Update r_tables aliases.
     433            for alias in change_map:
     434                if alias in r_tables:
     435                    r_tables.remove(alias)
     436                    r_tables.add(change_map[alias])
     437            # Find aliases that are exclusive to rhs or lhs.
     438            # These are promoted to outer joins.
     439            outer_aliases = (l_tables | r_tables) - (l_tables & r_tables)
     440            for alias in outer_aliases:
     441                self.promote_alias(alias, True)
    434442
    435443        # Now relabel a copy of the rhs where-clause and add it to the current
    436444        # one.
  • tests/regressiontests/queries/tests.py

     
    11import unittest
    2 from models import Tag, Annotation
    3 from django.db.models import Count
     2from models import Tag, Annotation, ObjectA, ObjectB, ObjectC
     3from django.db.models import Count, Q
    44
    55class QuerysetOrderedTests(unittest.TestCase):
    66    """
     
    2424        qs = Annotation.objects.annotate(num_notes=Count('notes'))
    2525        self.assertEqual(qs.ordered, False)
    2626        self.assertEqual(qs.order_by('num_notes').ordered, True)
    27        
    28  No newline at end of file
     27
     28
     29class UnionTests(unittest.TestCase):
     30    """
     31    Tests for the union of two querysets.
     32    Bug #12252.
     33    """
     34   
     35    def __init__(self, *args, **kwargs):
     36        super(UnionTests, self).__init__(*args, **kwargs)
     37
     38    def setUp(self):
     39        # Don't bother setting up if already done
     40        if ObjectA.objects.all():
     41            return
     42        objectas = []
     43        objectbs = []
     44        objectcs = []
     45        a_info = ['one', 'two', 'three']
     46        for name in a_info:
     47            o = ObjectA(name=name)
     48            o.save()
     49            objectas.append(o)
     50        b_info = [('un', 1, objectas[0]), ('deux', 2, objectas[0]), ('trois', 3, objectas[2])]
     51        for name, number, objecta in b_info:
     52            o = ObjectB(name=name, number=number, objecta=objecta)
     53            o.save()
     54            objectbs.append(o)
     55        c_info = [('ein', objectas[2], objectbs[2]), ('zwei', objectas[1], objectbs[1])]
     56        for name, objecta, objectb in c_info:
     57            o = ObjectC(name=name, objecta=objecta, objectb=objectb)
     58            o.save()
     59            objectcs.append(o)
     60
     61    def check_union(self, model, Q1, Q2):
     62        filter = model.objects.filter
     63        self.assertEqual(set(filter(Q1) | filter(Q2)), set(filter(Q1 | Q2)))
     64        self.assertEqual(set(filter(Q2) | filter(Q1)), set(filter(Q1 | Q2)))
     65       
     66    def test_A_AB(self):
     67        Q1 = Q(name='two')
     68        Q2 = Q(objectb__name='deux')
     69        self.check_union(ObjectA, Q1, Q2)
     70       
     71    def test_A_AB2(self):
     72        Q1 = Q(name='two')
     73        Q2 = Q(objectb__name='deux', objectb__number=2)
     74        self.check_union(ObjectA, Q1, Q2)
     75
     76    def test_AB_ACB(self):
     77        Q1 = Q(objectb__name='deux')
     78        Q2 = Q(objectc__objectb__name='deux')
     79        self.check_union(ObjectA, Q1, Q2)
     80
     81    def test_BAB_BAC(self):
     82        Q1 = Q(objecta__objectb__name='deux')
     83        Q2 = Q(objecta__objectc__name='ein')
     84        self.check_union(ObjectB, Q1, Q2)
     85       
     86    def test_BAB_BACB(self):
     87        Q1 = Q(objecta__objectb__name='deux')
     88        Q2 = Q(objecta__objectc__objectb__name='trois')
     89        self.check_union(ObjectB, Q1, Q2)
     90
     91    def test_BA_BCA__BAB_BAC_BCA(self):
     92        Q1 = Q(objecta__name='one', objectc__objecta__name='two')
     93        Q2 = Q(objecta__objectc__name='ein', objectc__objecta__name='three', objecta__objectb__name='trois')
     94        self.check_union(ObjectB, Q1, Q2)
  • tests/regressiontests/queries/models.py

     
    275275    def __unicode__(self):
    276276        return self.name
    277277
     278# Bug #12252
     279class ObjectA(models.Model):
     280    name = models.CharField(max_length=50)
    278281
     282    def __unicode__(self):
     283        return self.name
     284
     285class ObjectB(models.Model):
     286    name = models.CharField(max_length=50)
     287    objecta = models.ForeignKey(ObjectA)
     288    number = models.PositiveSmallIntegerField()
     289
     290    def __unicode__(self):
     291        return self.name
     292
     293class ObjectC(models.Model):
     294    name = models.CharField(max_length=50)
     295    objecta = models.ForeignKey(ObjectA)
     296    objectb = models.ForeignKey(ObjectB)
     297
     298    def __unicode__(self):
     299        return self.name
     300
     301
    279302__test__ = {'API_TESTS':"""
    280303>>> # Regression for #13156 -- exists() queries have minimal SQL
    281304>>> from django.db import connection
Back to Top