Ticket #12252: 12252.diff

File 12252.diff, 5.7 KB (added by Karen Tracey, 13 years ago)

Updated patch to apply cleanly

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

     
    446446            "Cannot combine a unique query with a non-unique query."
    447447
    448448        self.remove_inherited_models()
     449        l_tables = set([a for a in self.tables if self.alias_refcount[a]])
     450        r_tables = set([a for a in rhs.tables if rhs.alias_refcount[a]])
    449451        # Work out how to relabel the rhs aliases, if necessary.
    450452        change_map = {}
    451453        used = set()
     
    463465            first = False
    464466
    465467        # So that we don't exclude valid results in an "or" query combination,
    466         # the first join that is exclusive to the lhs (self) must be converted
     468        # all joins exclusive to either the lhs or the rhs must be converted
    467469        # to an outer join.
    468470        if not conjunction:
    469             for alias in self.tables[1:]:
    470                 if self.alias_refcount[alias] == 1:
    471                     self.promote_alias(alias, True)
    472                     break
     471            # Update r_tables aliases.
     472            for alias in change_map:
     473                if alias in r_tables:
     474                    r_tables.remove(alias)
     475                    r_tables.add(change_map[alias])
     476            # Find aliases that are exclusive to rhs or lhs.
     477            # These are promoted to outer joins.
     478            outer_aliases = (l_tables | r_tables) - (l_tables & r_tables)
     479            for alias in outer_aliases:
     480                self.promote_alias(alias, True)
    473481
    474482        # Now relabel a copy of the rhs where-clause and add it to the current
    475483        # one.
  • tests/regressiontests/queries/tests.py

     
    1414from models import (Annotation, Article, Author, Celebrity, Child, Cover, Detail,
    1515    DumbCategory, ExtraInfo, Fan, Item, LeafA, LoopX, LoopZ, ManagedModel,
    1616    Member, NamedCategory, Note, Number, Plaything, PointerA, Ranking, Related,
    17     Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Node)
     17    Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Node, ObjectA, ObjectB,
     18    ObjectC)
    1819
    1920
    2021class BaseQuerysetTest(TestCase):
     
    16581659            Number.objects.filter(num__in=numbers).count(),
    16591660            2500
    16601661        )
     1662
     1663class UnionTests(unittest.TestCase):
     1664    """
     1665    Tests for the union of two querysets.
     1666    Bug #12252.
     1667    """
     1668   
     1669    def __init__(self, *args, **kwargs):
     1670        super(UnionTests, self).__init__(*args, **kwargs)
     1671
     1672    def setUp(self):
     1673        # Don't bother setting up if already done
     1674        if ObjectA.objects.all():
     1675            return
     1676        objectas = []
     1677        objectbs = []
     1678        objectcs = []
     1679        a_info = ['one', 'two', 'three']
     1680        for name in a_info:
     1681            o = ObjectA(name=name)
     1682            o.save()
     1683            objectas.append(o)
     1684        b_info = [('un', 1, objectas[0]), ('deux', 2, objectas[0]), ('trois', 3, objectas[2])]
     1685        for name, number, objecta in b_info:
     1686            o = ObjectB(name=name, number=number, objecta=objecta)
     1687            o.save()
     1688            objectbs.append(o)
     1689        c_info = [('ein', objectas[2], objectbs[2]), ('zwei', objectas[1], objectbs[1])]
     1690        for name, objecta, objectb in c_info:
     1691            o = ObjectC(name=name, objecta=objecta, objectb=objectb)
     1692            o.save()
     1693            objectcs.append(o)
     1694
     1695    def check_union(self, model, Q1, Q2):
     1696        filter = model.objects.filter
     1697        self.assertEqual(set(filter(Q1) | filter(Q2)), set(filter(Q1 | Q2)))
     1698        self.assertEqual(set(filter(Q2) | filter(Q1)), set(filter(Q1 | Q2)))
     1699       
     1700    def test_A_AB(self):
     1701        Q1 = Q(name='two')
     1702        Q2 = Q(objectb__name='deux')
     1703        self.check_union(ObjectA, Q1, Q2)
     1704       
     1705    def test_A_AB2(self):
     1706        Q1 = Q(name='two')
     1707        Q2 = Q(objectb__name='deux', objectb__number=2)
     1708        self.check_union(ObjectA, Q1, Q2)
     1709
     1710    def test_AB_ACB(self):
     1711        Q1 = Q(objectb__name='deux')
     1712        Q2 = Q(objectc__objectb__name='deux')
     1713        self.check_union(ObjectA, Q1, Q2)
     1714
     1715    def test_BAB_BAC(self):
     1716        Q1 = Q(objecta__objectb__name='deux')
     1717        Q2 = Q(objecta__objectc__name='ein')
     1718        self.check_union(ObjectB, Q1, Q2)
     1719       
     1720    def test_BAB_BACB(self):
     1721        Q1 = Q(objecta__objectb__name='deux')
     1722        Q2 = Q(objecta__objectc__objectb__name='trois')
     1723        self.check_union(ObjectB, Q1, Q2)
     1724
     1725    def test_BA_BCA__BAB_BAC_BCA(self):
     1726        Q1 = Q(objecta__name='one', objectc__objecta__name='two')
     1727        Q2 = Q(objecta__objectc__name='ein', objectc__objecta__name='three', objecta__objectb__name='trois')
     1728        self.check_union(ObjectB, Q1, Q2)
     1729
  • tests/regressiontests/queries/models.py

     
    294294
    295295    def __unicode__(self):
    296296        return u"%s" % self.num
     297
     298# Bug #12252
     299class ObjectA(models.Model):
     300    name = models.CharField(max_length=50)
     301 
     302    def __unicode__(self):
     303        return self.name
     304
     305class ObjectB(models.Model):
     306    name = models.CharField(max_length=50)
     307    objecta = models.ForeignKey(ObjectA)
     308    number = models.PositiveSmallIntegerField()
     309
     310    def __unicode__(self):
     311        return self.name
     312
     313class ObjectC(models.Model):
     314    name = models.CharField(max_length=50)
     315    objecta = models.ForeignKey(ObjectA)
     316    objectb = models.ForeignKey(ObjectB)
     317
     318    def __unicode__(self):
     319       return self.name
Back to Top