Opened 8 years ago
Last modified 8 years ago
#27536 closed Bug
order_by('pk') is not obeyed if superclass has default ordering — at Initial Version
Reported by: | Andrew Dodd | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 1.10 |
Severity: | Normal | Keywords: | order_by ordering |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Using 'pk' as an order_by() target does not work if the superclass has a default ordering specified on its 'Meta' class.
For example, with the following classes:
class SuperWithoutOrdering(models.Model): name = models.CharField(max_length=20) extra = models.CharField(max_length=20) class SuperWithOrdering(models.Model): name = models.CharField(max_length=20) extra = models.CharField(max_length=20) class Meta: ordering = ('name', 'extra') class SubFromSuperWithoutOrdering(SuperWithoutOrdering): anything = models.CharField(max_length=20) class SubFromSuperWithOrdering(SuperWithOrdering): anything = models.CharField(max_length=20) def __str__(self): # so test output is readable return 'SubWithOrdering - name:{} extra:{} anything:{}'.format( self.name, self.extra, self.anything)
Then:
SubFromSuperWithoutOrdering.objects.all().order_by('pk') # works, ordering by id SubFromSuperWithOrdering.object.all().order_by('pk') # does not work, it just orders by 'name' then 'extra'
I have two tests that demonstrate this.
One that compares the 'ORDER BY' clause of the actual SQL queries:
class TestOrderByClause(TestCase): def test_it_orders_by_pk_and_id_are_the_same_if_super_does_not_have_ordering(self): qs = SubFromSuperWithoutOrdering.objects.all() by_pk = str(qs.order_by('pk').query).split('ORDER BY')[1] by_id = str(qs.order_by('id').query).split('ORDER BY')[1] self.assertEquals(by_pk, by_id) # PASSES def test_it_orders_by_pk_and_id_are_the_same_if_super_does_have_ordering(self): qs = SubFromSuperWithOrdering.objects.all() by_pk = str(qs.order_by('pk').query).split('ORDER BY')[1] by_id = str(qs.order_by('id').query).split('ORDER BY')[1] self.assertEquals(by_pk, by_id) # FAILS # Alternative form? def test_order_by_clause_is_the_same_if_using_pk_or_id(self): for test_class in [SubFromSuperWithoutOrdering, SubFromSuperWithOrdering]: qs = test_class.objects.all() by_pk = str(qs.order_by('pk').query).split('ORDER BY')[1] by_id = str(qs.order_by('id').query).split('ORDER BY')[1] self.assertEquals(by_pk, by_id, 'Failed for {}'.format(test_class))
And one that compares the order of the items actually returned from executing the query:
class TestOrderingOfQuerySetWhenSuperclassHasDefaultOrdering(TestCase): def setUp(self): def make_obj(name, extra, anything): return SubFromSuperWithOrdering.objects.create(name=name, extra=extra, anything=anything) self.zoot2 = make_obj( 'Zoot', 'ZZZ - Unknown', 'fourth by super ordering, first by pk') self.zoot1 = make_obj( 'Zoot', 'AAA - Handmaiden', 'third by super ordering, second by pk') self.arthur2 = make_obj('Arthur', 'ZZZ - Imposter', 'second by super ordering, third by pk') self.arthur1 = make_obj('Arthur', 'AAA - The King', 'first by super ordering, fourth by pk') def test_it_uses_superclass_ordering_by_default(self): expected = [self.arthur1, self.arthur2, self.zoot1, self.zoot2] actual = list(SubFromSuperWithOrdering.objects.all()) self.assertEqual(expected, actual) def test_it_correctly_orders_with_id(self): expected = [self.zoot2, self.zoot1, self.arthur2, self.arthur1] actual = list(SubFromSuperWithOrdering.objects.all().order_by('id')) self.assertEqual(expected, actual) def test_it_correctly_orders_with_pk(self): expected = [self.zoot2, self.zoot1, self.arthur2, self.arthur1] actual = list(SubFromSuperWithOrdering.objects.all().order_by('pk')) self.assertEqual(expected, actual)
I'm not very sure where to go looking to fix this. The workaround is to specify the name of the field used as the PK instead of using 'pk'.