Code

Opened 3 years ago

Closed 3 years ago

#15807 closed Bug (wontfix)

order by 'pk' alias doesn't work with inherited models when parent has an ordering set

Reported by: mat Owned by: nobody
Component: Database layer (models, ORM) Version: master
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX:

Description

Consider the following models:

from django.db import models

class Foo(models.Model):
    bar = models.PositiveSmallIntegerField(default=1)
    class Meta:
        ordering = ['bar']
        
class Child(Foo):
    barchild = models.PositiveSmallIntegerField(default=1)

Trying to get the Child objects, ordering by 'id', works:

>>> str(Child.objects.order_by('id').query)
'SELECT "test_pk_foo"."id", "test_pk_foo"."bar", "test_pk_child"."foo_ptr_id", "test_pk_child"."barchild" FROM "test_pk_child" INNER JOIN #"test_pk_foo" ON ("test_pk_child"."foo_ptr_id" = "test_pk_foo"."id") ORDER BY "test_pk_child"."foo_ptr_id" ASC'

However, trying to order by 'pk' doesn't (see the ORDER BY clause):

>>> str(Child.objects.order_by('pk').query)
'SELECT "test_pk_foo"."id", "test_pk_foo"."bar", "test_pk_child"."foo_ptr_id", "test_pk_child"."barchild" FROM "test_pk_child" INNER JOIN #"test_pk_foo" ON ("test_pk_child"."foo_ptr_id" = "test_pk_foo"."id") ORDER BY "test_pk_foo"."bar" ASC'

The default ordering from the parent is used instead! Removing the ordering property or using id instead of pk works, but I expected pk to work.

I reproduced this with django 1.2, 1.3 and trunk, with sqlite and postgresql_psycopg2 backends.

Attachments (0)

Change History (2)

comment:1 Changed 3 years ago by mat

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Summary changed from 'pk' alias doesn't work with inherited models when parent has an ordering set to order by 'pk' alias doesn't work with inherited models when parent has an ordering set

comment:2 Changed 3 years ago by jacob

  • Easy pickings unset
  • Resolution set to wontfix
  • Status changed from new to closed

So here's why this is happening:

pk is translated to "the name of the primary key column". Child, as a concrete subclass, has a primary key that has a foreign key (technically a OneToOneField with parent_link=True) to Foo. In other words, your models, with the field implicitly added by the subclassing, actually are like:

class Foo(models.Model):
    bar = models.PositiveSmallIntegerField(default=1)
        
class Child(models.Model):
    foo_ptr = models.OneToOneField(Foo, parent_link=True)
    barchild = models.PositiveSmallIntegerField(default=1)

This means that Child.objects.order_by("pk") actually translates to Child.objects.order_by("foo_ptr"). Remember, foo_ptr is a foreign key (one-to-one fields are just a special kind of foreign key). This is important, because order_by("some_foreign_key") translates to "order by the default ordering for the object pointed to by the foreign key". As the documentation says:

If you try to order by a field that is a relation to another model, Django will use the default ordering on the related model (or order by the related model's primary key if there is no Meta.ordering specified.

The object pointed to, in this case, is Foo. So Child.objects.order_by("pk") is in fact working correctly, if a bit counterintuitively.

We could change this, but it would introduce other subtle counterintuitive problems and would quite likely be backwards-incompatible. So I'm going to mark this wontfix. I know it's not ideal, but I'm afraid the cure is worse than the disease here.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.