#27536: order_by('pk') is not obeyed if superclass has default ordering
-------------------------------------+-------------------------------------
     Reporter:  Andrew Dodd          |                    Owner:  nobody
         Type:  Bug                  |                   Status:  new
    Component:  Database layer       |                  Version:  1.10
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:  order_by ordering    |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Description changed by Andrew Dodd:

Old 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:
> {{{
> #!python
> 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:
> {{{
> #!python
> 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:
> {{{
> #!python
> 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:
>
> {{{
> #!python
> 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'.

New 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:
 {{{
 #!python
 class SuperWithoutOrdering(models.Model):
     name = models.CharField(max_length=200)
     extra = models.CharField(max_length=200)


 class SuperWithOrdering(models.Model):
     name = models.CharField(max_length=200)
     extra = models.CharField(max_length=200)

     class Meta:
         ordering = ('name', 'extra')


 class SubFromSuperWithoutOrdering(SuperWithoutOrdering):
     anything = models.CharField(max_length=200)


 class SubFromSuperWithOrdering(SuperWithOrdering):
     anything = models.CharField(max_length=200)

     def __str__(self):
         # so test output is readable
         return 'SubWithOrdering - name:{} extra:{} anything:{}'.format(
                 self.name, self.extra, self.anything)
 }}}

 Then:
 {{{
 #!python
 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:
 {{{
 #!python
 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:

 {{{
 #!python
 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'.

--

--
Ticket URL: <https://code.djangoproject.com/ticket/27536#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/068.9ad1bb3ef525d58a371245b160f56e8a%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to