Django sort queryset by related model field

2.7k Views Asked by At

I have the following models (abbreviated for clarity):

class Order(models.Model):
    some fields

class OrderStatus(models.Model):
    order = models.ForiegnKey(Order)
    status = models.CharField(choices=['ORDERED', 'IN_TRANSIT', 'RECEIVED'])
    time = models.DateTimeField()

I would like to sort all Orders that contain all three OrderStatuses by their order received time.

In other words, select the orders that have records of Ordered, In Transit, and Received like so:

Order.objects.filter(orderstatus__status=OrderStatus.ORDERED)
             .filter(orderstatus__status=OrderStatus.IN_TRANSIT)
             .filter(orderstatus__status=OrderStatus.RECEIVED)

... and then sort them by the time field of their related OrderStatus model for which status=OrderStatus.RECEIVED.

This is where I'm stuck. I have read the Django docs on the .extra() queryset modifier and direct SQL injection, but I'm still at a loss. Could I achieve it with an annotated field and Q objects or am I better going the .extra route?

1

There are 1 best solutions below

0
On BEST ANSWER

Didn't you try to do like this?

Order.objects.filter(orderstatus__status=OrderStatus.ORDERED)
             .filter(orderstatus__status=OrderStatus.IN_TRANSIT)
             .filter(orderstatus__status=OrderStatus.RECEIVED)
             .order_by('orderstatus__time')

On my models it worked as expected - order_by picked the last joined orderstatus just as you need. If you're not sure you can check the real query like this (in django shell):

from django.db import connection
# perform query
print(connection.queries)

Also it can be done like this:

OrderStatus.objects.filter(status=OrderStatus.RECEIVED)
                   .order_by('time').select_related('order')
                   .filter(order__orderstatus__status=OrderStatus.ORDERED)
                   .filter(order__orderstatus__status=OrderStatus.IN_TRANSIT)