Opened 13 years ago

Closed 13 years ago

#15807 closed Bug (wontfix)

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

Reported by: Mathieu Pillard Owned by: nobody
Component: Database layer (models, ORM) Version: dev
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: no

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.

Change History (2)

comment:1 by Mathieu Pillard, 13 years ago

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

comment:2 by Jacob, 13 years ago

Easy pickings: unset
Resolution: wontfix
Status: newclosed

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.

Note: See TracTickets for help on using tickets.
Back to Top