Opened 8 years ago

Closed 5 years ago

#9415 closed Uncategorized (wontfix)

QuerySet.order_by() should be chainable

Reported by: Joey Wilhelm Owned by: nobody
Component: Database layer (models, ORM) Version: 1.0
Severity: Normal Keywords:
Cc: Triage Stage: Design decision needed
Has patch: yes Needs documentation: yes
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


I have a case where it would be very convenient to be able to call order_by() multiple times, appending to the order by clause with each successive call; i.e.,

foo = Model.objects.all().order_by('field1')
if bar:
    foo = foo.order_by('-field2')

Currently, the second call to order_by() explicitly overrides the previous order. This is not the behavior I would expect, as most (all?) other methods on a QuerySet are chainable.

If it is decided that chaining will not be allowed with order_by(), then I think that this fact should at least be documented. Had I not been printing out my connection's queries when I ran into this problem, it may have taken me some time to figure out exactly what was going on.

Attachments (1)

order-by-reset.diff (2.1 KB) - added by Alex Gaynor 8 years ago.
patch that adds the proposed reset kwarg, backwards compatible

Download all attachments as: .zip

Change History (11)

comment:1 Changed 8 years ago by Alex Gaynor

Needs documentation: unset
Needs tests: unset
Patch needs improvement: unset
Triage Stage: UnreviewedDesign decision needed

having them just chainable by default would be backwards incompatible, so it really can't be done, however perhaps their could be a kwarg to tell order_by not to remove the previous ordering(reset=False perhaps?).

comment:2 Changed 8 years ago by Alex Gaynor

FWIW the code changes needed would be VERY minimal once a good API is determined.

Changed 8 years ago by Alex Gaynor

Attachment: order-by-reset.diff added

patch that adds the proposed reset kwarg, backwards compatible

comment:3 Changed 8 years ago by Alex Gaynor

Has patch: set
Needs documentation: set

comment:4 Changed 8 years ago by Chris Beaven

Bikeshedding: Explicitly setting reset=False seems weird (a default of True is the odd bit, I guess).

How about if it was append=True instead?

Agreed that if this ticket is wontfixed, the docs should at least be clarified.

comment:5 Changed 8 years ago by Alex Gaynor

SmileyChris: bikeshed all you want, I let people who know how to design APIs do that :)

comment:6 Changed 8 years ago by anonymous

To get around this until the patch lands, we currently have an ordering = [] set and as the orderings are picked up append to ordering. The final step is then qset = qset.order_by(*ordering).

comment:7 Changed 8 years ago by Malcolm Tredinnick

Resolution: wontfix
Status: newclosed

No, the current behaviour is deliberate. The reason is that chaining for ordering would actually behave very counter-intuitively, since ordering is left-to-right sensitive in terms of "most significant" to "least significant". Things added after the initial ordering end up having almost no effect on the result, because the leading ordering terms dominate.

I don't see it as really common enough that the major ordering is going to be added in one place and minor tweaking of the far right, inisignificant terms will happen later that it's worth complicating the UI here.

A docs patch would be fine (in another ticket so that we can review it in isolation), but the current behaviour is quite intentional and it would be counter-productive to change it.

comment:8 Changed 5 years ago by Brillgen Developers

Easy pickings: unset
Resolution: wontfix
Severity: Normal
Status: closedreopened
Type: Uncategorized
UI/UX: unset

I'd run into the same problem without an easy solution ...I pass around a lazy orderset in a structure hierarchy of classes and the possibility of adding an order_by secondary clauses for different cases.

@mtredinnick: I understand you may not have use cases where you can see the value in this, however providing an append=True option will neither bloat the code nor have a performance impact.

comment:9 Changed 5 years ago by Brillgen Developers

Easy pickings: set

Just to clarify futher:

Currently this works (and secondary order is very significant when you have lots of items matching on the first order key)
foo = Model.objects.all().order_by('field1', 'field2')

all that is being proposed is a fully backward compatible minor enhancement with no negative impact whatsoevery:

foo = Model.objects.all().order_by('field1')
if bar:

foo = foo.order_by('field2', append=True) # For use cases where the original order_by is not known...this again has no harm, and for people who don't need it don't need to use it!!

comment:10 Changed 5 years ago by Aymeric Augustin

Easy pickings: unset
Resolution: wontfix
Status: reopenedclosed

Please don't reopen a ticket closed by a core developer.

If you want to reverse a decision made by a core developer, you should write to the django-developers mailing-list.

Take a look at the contribution guidelines in the docs for more information. Thanks :)

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