Loading tests/regressiontests/extra_regress/models.py +0 −177 Original line number Diff line number Diff line Loading @@ -4,9 +4,6 @@ import django.utils.copycompat as copy from django.contrib.auth.models import User from django.db import models from django.db.models.query import Q from django.utils.datastructures import SortedDict class RevisionableModel(models.Model): base = models.ForeignKey('self', null=True) Loading Loading @@ -41,177 +38,3 @@ class TestObject(models.Model): def __unicode__(self): return u'TestObject: %s,%s,%s' % (self.first,self.second,self.third) __test__ = {"API_TESTS": """ # Regression tests for #7314 and #7372 >>> rm = RevisionableModel.objects.create(title='First Revision', when=datetime.datetime(2008, 9, 28, 10, 30, 0)) >>> rm.pk, rm.base.pk (1, 1) >>> rm2 = rm.new_revision() >>> rm2.title = "Second Revision" >>> rm.when = datetime.datetime(2008, 9, 28, 14, 25, 0) >>> rm2.save() >>> print u"%s of %s" % (rm2.title, rm2.base.title) Second Revision of First Revision >>> rm2.pk, rm2.base.pk (2, 1) Queryset to match most recent revision: >>> qs = RevisionableModel.objects.extra(where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % {'table': RevisionableModel._meta.db_table,}],) >>> qs [<RevisionableModel: Second Revision (2, 1)>] Queryset to search for string in title: >>> qs2 = RevisionableModel.objects.filter(title__contains="Revision") >>> qs2 [<RevisionableModel: First Revision (1, 1)>, <RevisionableModel: Second Revision (2, 1)>] Following queryset should return the most recent revision: >>> qs & qs2 [<RevisionableModel: Second Revision (2, 1)>] >>> u = User.objects.create_user(username="fred", password="secret", email="fred@example.com") # General regression tests: extra select parameters should stay tied to their # corresponding select portions. Applies when portions are updated or otherwise # moved around. >>> qs = User.objects.extra(select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select_params=(1, 3)) >>> qs = qs.extra(select={"beta": 4}) >>> qs = qs.extra(select={"alpha": "%s"}, select_params=[5]) >>> result = {'alpha': 5, 'beta': 4, 'gamma': 3} >>> list(qs.filter(id=u.id).values('alpha', 'beta', 'gamma')) == [result] True # Regression test for #7957: Combining extra() calls should leave the # corresponding parameters associated with the right extra() bit. I.e. internal # dictionary must remain sorted. >>> User.objects.extra(select={"alpha": "%s"}, select_params=(1,)).extra(select={"beta": "%s"}, select_params=(2,))[0].alpha 1 >>> User.objects.extra(select={"beta": "%s"}, select_params=(1,)).extra(select={"alpha": "%s"}, select_params=(2,))[0].alpha 2 # Regression test for #7961: When not using a portion of an extra(...) in a # query, remove any corresponding parameters from the query as well. >>> list(User.objects.extra(select={"alpha": "%s"}, select_params=(-6,)).filter(id=u.id).values_list('id', flat=True)) == [u.id] True # Regression test for #8063: limiting a query shouldn't discard any extra() # bits. >>> qs = User.objects.all().extra(where=['id=%s'], params=[u.id]) >>> qs [<User: fred>] >>> qs[:1] [<User: fred>] # Regression test for #8039: Ordering sometimes removed relevant tables from # extra(). This test is the critical case: ordering uses a table, but then # removes the reference because of an optimisation. The table should still be # present because of the extra() call. >>> Order.objects.extra(where=["username=%s"], params=["fred"], tables=["auth_user"]).order_by('created_by') [] # Regression test for #8819: Fields in the extra(select=...) list should be # available to extra(order_by=...). >>> User.objects.filter(pk=u.id).extra(select={'extra_field': 1}).distinct() [<User: fred>] >>> User.objects.filter(pk=u.id).extra(select={'extra_field': 1}, order_by=['extra_field']) [<User: fred>] >>> User.objects.filter(pk=u.id).extra(select={'extra_field': 1}, order_by=['extra_field']).distinct() [<User: fred>] # When calling the dates() method on a queryset with extra selection columns, # we can (and should) ignore those columns. They don't change the result and # cause incorrect SQL to be produced otherwise. >>> RevisionableModel.objects.extra(select={"the_answer": 'id'}).dates('when', 'month') [datetime.datetime(2008, 9, 1, 0, 0)] # Regression test for #10256... If there is a values() clause, Extra columns are # only returned if they are explicitly mentioned. >>> TestObject(first='first', second='second', third='third').save() >>> list(TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values()) == [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': 1, 'first': u'first'}] True # Extra clauses after an empty values clause are still included >>> list(TestObject.objects.values().extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third'))))) == [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': 1, 'first': u'first'}] True # Extra columns are ignored if not mentioned in the values() clause >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values('first', 'second') [{'second': u'second', 'first': u'first'}] # Extra columns after a non-empty values() clause are ignored >>> TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))) [{'second': u'second', 'first': u'first'}] # Extra columns can be partially returned >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values('first', 'second', 'foo') [{'second': u'second', 'foo': u'first', 'first': u'first'}] # Also works if only extra columns are included >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values('foo', 'whiz') [{'foo': u'first', 'whiz': u'third'}] # Values list works the same way # All columns are returned for an empty values_list() >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list() [(u'first', u'second', u'third', 1, u'first', u'second', u'third')] # Extra columns after an empty values_list() are still included >>> TestObject.objects.values_list().extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))) [(u'first', u'second', u'third', 1, u'first', u'second', u'third')] # Extra columns ignored completely if not mentioned in values_list() >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('first', 'second') [(u'first', u'second')] # Extra columns after a non-empty values_list() clause are ignored completely >>> TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))) [(u'first', u'second')] >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('second', flat=True) [u'second'] # Only the extra columns specified in the values_list() are returned >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('first', 'second', 'whiz') [(u'first', u'second', u'third')] # ...also works if only extra columns are included >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('foo','whiz') [(u'first', u'third')] >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('whiz', flat=True) [u'third'] # ... and values are returned in the order they are specified >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('whiz','foo') [(u'third', u'first')] >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('first','id') [(u'first', 1)] >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('whiz', 'first', 'bar', 'id') [(u'third', u'first', u'second', 1)] # Regression for #10847: the list of extra columns can always be accurately evaluated. # Using an inner query ensures that as_sql() is producing correct output # without requiring full evaluation and execution of the inner query. >>> TestObject.objects.extra(select={'extra': 1}).values('pk') [{'pk': 1}] >>> TestObject.objects.filter(pk__in=TestObject.objects.extra(select={'extra': 1}).values('pk')) [<TestObject: TestObject: first,second,third>] >>> TestObject.objects.values('pk').extra(select={'extra': 1}) [{'pk': 1}] >>> TestObject.objects.filter(pk__in=TestObject.objects.values('pk').extra(select={'extra': 1})) [<TestObject: TestObject: first,second,third>] >>> pk = TestObject.objects.get().pk >>> TestObject.objects.filter(pk=pk) | TestObject.objects.extra(where=["id > %s"], params=[pk]) [<TestObject: TestObject: first,second,third>] """} tests/regressiontests/extra_regress/tests.py 0 → 100644 +314 −0 Original line number Diff line number Diff line from django.test import TestCase from django.utils.datastructures import SortedDict from django.contrib.auth.models import User from regressiontests.extra_regress.models import TestObject, Order, \ RevisionableModel import datetime class ExtraRegressTests(TestCase): def setUp(self): self.u = User.objects.create_user( username="fred", password="secret", email="fred@example.com" ) def test_regression_7314_7372(self): """ Regression tests for #7314 and #7372 """ rm = RevisionableModel.objects.create( title='First Revision', when=datetime.datetime(2008, 9, 28, 10, 30, 0) ) self.assertEqual(rm.pk, rm.base.pk) rm2 = rm.new_revision() rm2.title = "Second Revision" rm.when = datetime.datetime(2008, 9, 28, 14, 25, 0) rm2.save() self.assertEqual(rm2.title, 'Second Revision') self.assertEqual(rm2.base.title, 'First Revision') self.assertNotEqual(rm2.pk, rm.pk) self.assertEqual(rm2.base.pk, rm.pk) # Queryset to match most recent revision: qs = RevisionableModel.objects.extra( where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % { 'table': RevisionableModel._meta.db_table, }] ) self.assertQuerysetEqual(qs, [('Second Revision', 'First Revision')], transform=lambda r: (r.title, r.base.title) ) # Queryset to search for string in title: qs2 = RevisionableModel.objects.filter(title__contains="Revision") self.assertQuerysetEqual(qs2, [ ('First Revision', 'First Revision'), ('Second Revision', 'First Revision'), ], transform=lambda r: (r.title, r.base.title) ) # Following queryset should return the most recent revision: self.assertQuerysetEqual(qs & qs2, [('Second Revision', 'First Revision')], transform=lambda r: (r.title, r.base.title) ) def test_extra_stay_tied(self): # Extra select parameters should stay tied to their corresponding # select portions. Applies when portions are updated or otherwise # moved around. qs = User.objects.extra( select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select_params=(1, 3) ) qs = qs.extra(select={"beta": 4}) qs = qs.extra(select={"alpha": "%s"}, select_params=[5]) self.assertEqual( list(qs.filter(id=self.u.id).values('alpha', 'beta', 'gamma')), [{'alpha': 5, 'beta': 4, 'gamma': 3}] ) def test_regression_7957(self): """ Regression test for #7957: Combining extra() calls should leave the corresponding parameters associated with the right extra() bit. I.e. internal dictionary must remain sorted. """ self.assertEqual( User.objects.extra(select={"alpha": "%s"}, select_params=(1,) ).extra(select={"beta": "%s"}, select_params=(2,))[0].alpha, 1) self.assertEqual( User.objects.extra(select={"beta": "%s"}, select_params=(1,) ).extra(select={"alpha": "%s"}, select_params=(2,))[0].alpha, 2) def test_regression_7961(self): """ Regression test for #7961: When not using a portion of an extra(...) in a query, remove any corresponding parameters from the query as well. """ self.assertEqual( list(User.objects.extra(select={"alpha": "%s"}, select_params=(-6,) ).filter(id=self.u.id).values_list('id', flat=True)), [self.u.id] ) def test_regression_8063(self): """ Regression test for #8063: limiting a query shouldn't discard any extra() bits. """ qs = User.objects.all().extra(where=['id=%s'], params=[self.u.id]) self.assertQuerysetEqual(qs, ['<User: fred>']) self.assertQuerysetEqual(qs[:1], ['<User: fred>']) def test_regression_8039(self): """ Regression test for #8039: Ordering sometimes removed relevant tables from extra(). This test is the critical case: ordering uses a table, but then removes the reference because of an optimisation. The table should still be present because of the extra() call. """ self.assertQuerysetEqual( Order.objects.extra(where=["username=%s"], params=["fred"], tables=["auth_user"] ).order_by('created_by'), [] ) def test_regression_8819(self): """ Regression test for #8819: Fields in the extra(select=...) list should be available to extra(order_by=...). """ self.assertQuerysetEqual( User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}).distinct(), ['<User: fred>'] ) self.assertQuerysetEqual( User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}, order_by=['extra_field']), ['<User: fred>'] ) self.assertQuerysetEqual( User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}, order_by=['extra_field']).distinct(), ['<User: fred>'] ) def test_dates_query(self): """ When calling the dates() method on a queryset with extra selection columns, we can (and should) ignore those columns. They don't change the result and cause incorrect SQL to be produced otherwise. """ rm = RevisionableModel.objects.create( title='First Revision', when=datetime.datetime(2008, 9, 28, 10, 30, 0) ) self.assertQuerysetEqual( RevisionableModel.objects.extra(select={"the_answer": 'id'}).dates('when', 'month'), ['datetime.datetime(2008, 9, 1, 0, 0)'] ) def test_values_with_extra(self): """ Regression test for #10256... If there is a values() clause, Extra columns are only returned if they are explicitly mentioned. """ obj = TestObject(first='first', second='second', third='third') obj.save() self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()), [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': obj.pk, 'first': u'first'}] ) # Extra clauses after an empty values clause are still included self.assertEqual( list(TestObject.objects.values().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': obj.pk, 'first': u'first'}] ) # Extra columns are ignored if not mentioned in the values() clause self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')), [{'second': u'second', 'first': u'first'}] ) # Extra columns after a non-empty values() clause are ignored self.assertEqual( list(TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [{'second': u'second', 'first': u'first'}] ) # Extra columns can be partially returned self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')), [{'second': u'second', 'foo': u'first', 'first': u'first'}] ) # Also works if only extra columns are included self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')), [{'foo': u'first', 'whiz': u'third'}] ) # Values list works the same way # All columns are returned for an empty values_list() self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')] ) # Extra columns after an empty values_list() are still included self.assertEqual( list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')] ) # Extra columns ignored completely if not mentioned in values_list() self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')), [(u'first', u'second')] ) # Extra columns after a non-empty values_list() clause are ignored completely self.assertEqual( list(TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [(u'first', u'second')] ) self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)), [u'second'] ) # Only the extra columns specified in the values_list() are returned self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')), [(u'first', u'second', u'third')] ) # ...also works if only extra columns are included self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')), [(u'first', u'third')] ) self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)), [u'third'] ) # ... and values are returned in the order they are specified self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')), [(u'third', u'first')] ) self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')), [(u'first', obj.pk)] ) self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')), [(u'third', u'first', u'second', obj.pk)] ) def test_regression_10847(self): """ Regression for #10847: the list of extra columns can always be accurately evaluated. Using an inner query ensures that as_sql() is producing correct output without requiring full evaluation and execution of the inner query. """ obj = TestObject(first='first', second='second', third='third') obj.save() self.assertEqual( list(TestObject.objects.extra(select={'extra': 1}).values('pk')), [{'pk': obj.pk}] ) self.assertQuerysetEqual( TestObject.objects.filter( pk__in=TestObject.objects.extra(select={'extra': 1}).values('pk') ), ['<TestObject: TestObject: first,second,third>'] ) self.assertEqual( list(TestObject.objects.values('pk').extra(select={'extra': 1})), [{'pk': obj.pk}] ) self.assertQuerysetEqual( TestObject.objects.filter( pk__in=TestObject.objects.values('pk').extra(select={'extra': 1}) ), ['<TestObject: TestObject: first,second,third>'] ) self.assertQuerysetEqual( TestObject.objects.filter(pk=obj.pk) | TestObject.objects.extra(where=["id > %s"], params=[obj.pk]), ['<TestObject: TestObject: first,second,third>'] ) Loading
tests/regressiontests/extra_regress/models.py +0 −177 Original line number Diff line number Diff line Loading @@ -4,9 +4,6 @@ import django.utils.copycompat as copy from django.contrib.auth.models import User from django.db import models from django.db.models.query import Q from django.utils.datastructures import SortedDict class RevisionableModel(models.Model): base = models.ForeignKey('self', null=True) Loading Loading @@ -41,177 +38,3 @@ class TestObject(models.Model): def __unicode__(self): return u'TestObject: %s,%s,%s' % (self.first,self.second,self.third) __test__ = {"API_TESTS": """ # Regression tests for #7314 and #7372 >>> rm = RevisionableModel.objects.create(title='First Revision', when=datetime.datetime(2008, 9, 28, 10, 30, 0)) >>> rm.pk, rm.base.pk (1, 1) >>> rm2 = rm.new_revision() >>> rm2.title = "Second Revision" >>> rm.when = datetime.datetime(2008, 9, 28, 14, 25, 0) >>> rm2.save() >>> print u"%s of %s" % (rm2.title, rm2.base.title) Second Revision of First Revision >>> rm2.pk, rm2.base.pk (2, 1) Queryset to match most recent revision: >>> qs = RevisionableModel.objects.extra(where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % {'table': RevisionableModel._meta.db_table,}],) >>> qs [<RevisionableModel: Second Revision (2, 1)>] Queryset to search for string in title: >>> qs2 = RevisionableModel.objects.filter(title__contains="Revision") >>> qs2 [<RevisionableModel: First Revision (1, 1)>, <RevisionableModel: Second Revision (2, 1)>] Following queryset should return the most recent revision: >>> qs & qs2 [<RevisionableModel: Second Revision (2, 1)>] >>> u = User.objects.create_user(username="fred", password="secret", email="fred@example.com") # General regression tests: extra select parameters should stay tied to their # corresponding select portions. Applies when portions are updated or otherwise # moved around. >>> qs = User.objects.extra(select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select_params=(1, 3)) >>> qs = qs.extra(select={"beta": 4}) >>> qs = qs.extra(select={"alpha": "%s"}, select_params=[5]) >>> result = {'alpha': 5, 'beta': 4, 'gamma': 3} >>> list(qs.filter(id=u.id).values('alpha', 'beta', 'gamma')) == [result] True # Regression test for #7957: Combining extra() calls should leave the # corresponding parameters associated with the right extra() bit. I.e. internal # dictionary must remain sorted. >>> User.objects.extra(select={"alpha": "%s"}, select_params=(1,)).extra(select={"beta": "%s"}, select_params=(2,))[0].alpha 1 >>> User.objects.extra(select={"beta": "%s"}, select_params=(1,)).extra(select={"alpha": "%s"}, select_params=(2,))[0].alpha 2 # Regression test for #7961: When not using a portion of an extra(...) in a # query, remove any corresponding parameters from the query as well. >>> list(User.objects.extra(select={"alpha": "%s"}, select_params=(-6,)).filter(id=u.id).values_list('id', flat=True)) == [u.id] True # Regression test for #8063: limiting a query shouldn't discard any extra() # bits. >>> qs = User.objects.all().extra(where=['id=%s'], params=[u.id]) >>> qs [<User: fred>] >>> qs[:1] [<User: fred>] # Regression test for #8039: Ordering sometimes removed relevant tables from # extra(). This test is the critical case: ordering uses a table, but then # removes the reference because of an optimisation. The table should still be # present because of the extra() call. >>> Order.objects.extra(where=["username=%s"], params=["fred"], tables=["auth_user"]).order_by('created_by') [] # Regression test for #8819: Fields in the extra(select=...) list should be # available to extra(order_by=...). >>> User.objects.filter(pk=u.id).extra(select={'extra_field': 1}).distinct() [<User: fred>] >>> User.objects.filter(pk=u.id).extra(select={'extra_field': 1}, order_by=['extra_field']) [<User: fred>] >>> User.objects.filter(pk=u.id).extra(select={'extra_field': 1}, order_by=['extra_field']).distinct() [<User: fred>] # When calling the dates() method on a queryset with extra selection columns, # we can (and should) ignore those columns. They don't change the result and # cause incorrect SQL to be produced otherwise. >>> RevisionableModel.objects.extra(select={"the_answer": 'id'}).dates('when', 'month') [datetime.datetime(2008, 9, 1, 0, 0)] # Regression test for #10256... If there is a values() clause, Extra columns are # only returned if they are explicitly mentioned. >>> TestObject(first='first', second='second', third='third').save() >>> list(TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values()) == [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': 1, 'first': u'first'}] True # Extra clauses after an empty values clause are still included >>> list(TestObject.objects.values().extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third'))))) == [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': 1, 'first': u'first'}] True # Extra columns are ignored if not mentioned in the values() clause >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values('first', 'second') [{'second': u'second', 'first': u'first'}] # Extra columns after a non-empty values() clause are ignored >>> TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))) [{'second': u'second', 'first': u'first'}] # Extra columns can be partially returned >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values('first', 'second', 'foo') [{'second': u'second', 'foo': u'first', 'first': u'first'}] # Also works if only extra columns are included >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values('foo', 'whiz') [{'foo': u'first', 'whiz': u'third'}] # Values list works the same way # All columns are returned for an empty values_list() >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list() [(u'first', u'second', u'third', 1, u'first', u'second', u'third')] # Extra columns after an empty values_list() are still included >>> TestObject.objects.values_list().extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))) [(u'first', u'second', u'third', 1, u'first', u'second', u'third')] # Extra columns ignored completely if not mentioned in values_list() >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('first', 'second') [(u'first', u'second')] # Extra columns after a non-empty values_list() clause are ignored completely >>> TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))) [(u'first', u'second')] >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('second', flat=True) [u'second'] # Only the extra columns specified in the values_list() are returned >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('first', 'second', 'whiz') [(u'first', u'second', u'third')] # ...also works if only extra columns are included >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('foo','whiz') [(u'first', u'third')] >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('whiz', flat=True) [u'third'] # ... and values are returned in the order they are specified >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('whiz','foo') [(u'third', u'first')] >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('first','id') [(u'first', 1)] >>> TestObject.objects.extra(select=SortedDict((('foo','first'),('bar','second'),('whiz','third')))).values_list('whiz', 'first', 'bar', 'id') [(u'third', u'first', u'second', 1)] # Regression for #10847: the list of extra columns can always be accurately evaluated. # Using an inner query ensures that as_sql() is producing correct output # without requiring full evaluation and execution of the inner query. >>> TestObject.objects.extra(select={'extra': 1}).values('pk') [{'pk': 1}] >>> TestObject.objects.filter(pk__in=TestObject.objects.extra(select={'extra': 1}).values('pk')) [<TestObject: TestObject: first,second,third>] >>> TestObject.objects.values('pk').extra(select={'extra': 1}) [{'pk': 1}] >>> TestObject.objects.filter(pk__in=TestObject.objects.values('pk').extra(select={'extra': 1})) [<TestObject: TestObject: first,second,third>] >>> pk = TestObject.objects.get().pk >>> TestObject.objects.filter(pk=pk) | TestObject.objects.extra(where=["id > %s"], params=[pk]) [<TestObject: TestObject: first,second,third>] """}
tests/regressiontests/extra_regress/tests.py 0 → 100644 +314 −0 Original line number Diff line number Diff line from django.test import TestCase from django.utils.datastructures import SortedDict from django.contrib.auth.models import User from regressiontests.extra_regress.models import TestObject, Order, \ RevisionableModel import datetime class ExtraRegressTests(TestCase): def setUp(self): self.u = User.objects.create_user( username="fred", password="secret", email="fred@example.com" ) def test_regression_7314_7372(self): """ Regression tests for #7314 and #7372 """ rm = RevisionableModel.objects.create( title='First Revision', when=datetime.datetime(2008, 9, 28, 10, 30, 0) ) self.assertEqual(rm.pk, rm.base.pk) rm2 = rm.new_revision() rm2.title = "Second Revision" rm.when = datetime.datetime(2008, 9, 28, 14, 25, 0) rm2.save() self.assertEqual(rm2.title, 'Second Revision') self.assertEqual(rm2.base.title, 'First Revision') self.assertNotEqual(rm2.pk, rm.pk) self.assertEqual(rm2.base.pk, rm.pk) # Queryset to match most recent revision: qs = RevisionableModel.objects.extra( where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % { 'table': RevisionableModel._meta.db_table, }] ) self.assertQuerysetEqual(qs, [('Second Revision', 'First Revision')], transform=lambda r: (r.title, r.base.title) ) # Queryset to search for string in title: qs2 = RevisionableModel.objects.filter(title__contains="Revision") self.assertQuerysetEqual(qs2, [ ('First Revision', 'First Revision'), ('Second Revision', 'First Revision'), ], transform=lambda r: (r.title, r.base.title) ) # Following queryset should return the most recent revision: self.assertQuerysetEqual(qs & qs2, [('Second Revision', 'First Revision')], transform=lambda r: (r.title, r.base.title) ) def test_extra_stay_tied(self): # Extra select parameters should stay tied to their corresponding # select portions. Applies when portions are updated or otherwise # moved around. qs = User.objects.extra( select=SortedDict((("alpha", "%s"), ("beta", "2"), ("gamma", "%s"))), select_params=(1, 3) ) qs = qs.extra(select={"beta": 4}) qs = qs.extra(select={"alpha": "%s"}, select_params=[5]) self.assertEqual( list(qs.filter(id=self.u.id).values('alpha', 'beta', 'gamma')), [{'alpha': 5, 'beta': 4, 'gamma': 3}] ) def test_regression_7957(self): """ Regression test for #7957: Combining extra() calls should leave the corresponding parameters associated with the right extra() bit. I.e. internal dictionary must remain sorted. """ self.assertEqual( User.objects.extra(select={"alpha": "%s"}, select_params=(1,) ).extra(select={"beta": "%s"}, select_params=(2,))[0].alpha, 1) self.assertEqual( User.objects.extra(select={"beta": "%s"}, select_params=(1,) ).extra(select={"alpha": "%s"}, select_params=(2,))[0].alpha, 2) def test_regression_7961(self): """ Regression test for #7961: When not using a portion of an extra(...) in a query, remove any corresponding parameters from the query as well. """ self.assertEqual( list(User.objects.extra(select={"alpha": "%s"}, select_params=(-6,) ).filter(id=self.u.id).values_list('id', flat=True)), [self.u.id] ) def test_regression_8063(self): """ Regression test for #8063: limiting a query shouldn't discard any extra() bits. """ qs = User.objects.all().extra(where=['id=%s'], params=[self.u.id]) self.assertQuerysetEqual(qs, ['<User: fred>']) self.assertQuerysetEqual(qs[:1], ['<User: fred>']) def test_regression_8039(self): """ Regression test for #8039: Ordering sometimes removed relevant tables from extra(). This test is the critical case: ordering uses a table, but then removes the reference because of an optimisation. The table should still be present because of the extra() call. """ self.assertQuerysetEqual( Order.objects.extra(where=["username=%s"], params=["fred"], tables=["auth_user"] ).order_by('created_by'), [] ) def test_regression_8819(self): """ Regression test for #8819: Fields in the extra(select=...) list should be available to extra(order_by=...). """ self.assertQuerysetEqual( User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}).distinct(), ['<User: fred>'] ) self.assertQuerysetEqual( User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}, order_by=['extra_field']), ['<User: fred>'] ) self.assertQuerysetEqual( User.objects.filter(pk=self.u.id).extra(select={'extra_field': 1}, order_by=['extra_field']).distinct(), ['<User: fred>'] ) def test_dates_query(self): """ When calling the dates() method on a queryset with extra selection columns, we can (and should) ignore those columns. They don't change the result and cause incorrect SQL to be produced otherwise. """ rm = RevisionableModel.objects.create( title='First Revision', when=datetime.datetime(2008, 9, 28, 10, 30, 0) ) self.assertQuerysetEqual( RevisionableModel.objects.extra(select={"the_answer": 'id'}).dates('when', 'month'), ['datetime.datetime(2008, 9, 1, 0, 0)'] ) def test_values_with_extra(self): """ Regression test for #10256... If there is a values() clause, Extra columns are only returned if they are explicitly mentioned. """ obj = TestObject(first='first', second='second', third='third') obj.save() self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values()), [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': obj.pk, 'first': u'first'}] ) # Extra clauses after an empty values clause are still included self.assertEqual( list(TestObject.objects.values().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [{'bar': u'second', 'third': u'third', 'second': u'second', 'whiz': u'third', 'foo': u'first', 'id': obj.pk, 'first': u'first'}] ) # Extra columns are ignored if not mentioned in the values() clause self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second')), [{'second': u'second', 'first': u'first'}] ) # Extra columns after a non-empty values() clause are ignored self.assertEqual( list(TestObject.objects.values('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [{'second': u'second', 'first': u'first'}] ) # Extra columns can be partially returned self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('first', 'second', 'foo')), [{'second': u'second', 'foo': u'first', 'first': u'first'}] ) # Also works if only extra columns are included self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values('foo', 'whiz')), [{'foo': u'first', 'whiz': u'third'}] ) # Values list works the same way # All columns are returned for an empty values_list() self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list()), [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')] ) # Extra columns after an empty values_list() are still included self.assertEqual( list(TestObject.objects.values_list().extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [(u'first', u'second', u'third', obj.pk, u'first', u'second', u'third')] ) # Extra columns ignored completely if not mentioned in values_list() self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second')), [(u'first', u'second')] ) # Extra columns after a non-empty values_list() clause are ignored completely self.assertEqual( list(TestObject.objects.values_list('first', 'second').extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third'))))), [(u'first', u'second')] ) self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('second', flat=True)), [u'second'] ) # Only the extra columns specified in the values_list() are returned self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first', 'second', 'whiz')), [(u'first', u'second', u'third')] ) # ...also works if only extra columns are included self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('foo','whiz')), [(u'first', u'third')] ) self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', flat=True)), [u'third'] ) # ... and values are returned in the order they are specified self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz','foo')), [(u'third', u'first')] ) self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('first','id')), [(u'first', obj.pk)] ) self.assertEqual( list(TestObject.objects.extra(select=SortedDict((('foo','first'), ('bar','second'), ('whiz','third')))).values_list('whiz', 'first', 'bar', 'id')), [(u'third', u'first', u'second', obj.pk)] ) def test_regression_10847(self): """ Regression for #10847: the list of extra columns can always be accurately evaluated. Using an inner query ensures that as_sql() is producing correct output without requiring full evaluation and execution of the inner query. """ obj = TestObject(first='first', second='second', third='third') obj.save() self.assertEqual( list(TestObject.objects.extra(select={'extra': 1}).values('pk')), [{'pk': obj.pk}] ) self.assertQuerysetEqual( TestObject.objects.filter( pk__in=TestObject.objects.extra(select={'extra': 1}).values('pk') ), ['<TestObject: TestObject: first,second,third>'] ) self.assertEqual( list(TestObject.objects.values('pk').extra(select={'extra': 1})), [{'pk': obj.pk}] ) self.assertQuerysetEqual( TestObject.objects.filter( pk__in=TestObject.objects.values('pk').extra(select={'extra': 1}) ), ['<TestObject: TestObject: first,second,third>'] ) self.assertQuerysetEqual( TestObject.objects.filter(pk=obj.pk) | TestObject.objects.extra(where=["id > %s"], params=[obj.pk]), ['<TestObject: TestObject: first,second,third>'] )