137

I upgraded from Django 1.10.4 to 1.11.1 and all of a sudden I'm getting a ton of these messages when I run my tests:

lib/python3.5/site-packages/rest_framework/pagination.py:208:
UnorderedObjectListWarning: 
Pagination may yield inconsistent results with an unordered object_list: 
<QuerySet [<Group: Requester>]>
paginator = self.django_paginator_class(queryset, page_size)

I've traced that back to the Django Pagination module: https://github.com/django/django/blob/master/django/core/paginator.py#L100

It seems to be related to my queryset code:

return get_user_model().objects.filter(id=self.request.user.id)

How can I find more details on this warning? It seems to be that I need to add a order_by(id) on the end of every filter, but I can't seem to find which code needs the order_by added (because the warning doesn't return a stack trace and so it happens randomly during my test run).

Thanks!

Edit:

So by using @KlausD. verbosity tip, I looked at a test causing this error:

response = self.client.get('/api/orders/')

This goes to OrderViewSet but none of the things in get_queryset cause it and nothing in serializer class causes it. I have other tests that use the same code to get /api/orders and those don't cause it.... What does DRF do after get_queryset?

https://github.com/encode/django-rest-framework/blob/master/rest_framework/pagination.py#L166

If I put a traceback into pagination then I get a whole bunch of stuff related to django rest framework but nothing that points back to which of my queries is triggering the order warning.

3
  • 1
    Usually it should be easy to find by the name of the test that causes the warning. You might want to run tests with verbosity (-v 2 on most test runners) Commented May 17, 2017 at 19:59
  • 1
    Looks for queries where you are doing an offset and limit but no order_by Commented May 17, 2017 at 20:14
  • Thanks @gipsy. I don't have any of those.... Commented May 17, 2017 at 20:21

8 Answers 8

211

So in order to fix this I had to find all of the all, offset, filter, and limit clauses and add a order_by clause to them. Some I fixed by adding a default ordering:

class Meta:
   ordering = ['-id']

In the ViewSets for Django Rest Framework (app/apiviews.py) I had to update all of the get_queryset methods as adding a default ordering didn't seem to work.

Sign up to request clarification or add additional context in comments.

7 Comments

Awesome, didn't know that DRF reads the model for ordering too <3
I do have a default ordering in my model but I still get this warning :|
My mistake. I had ordering in the parent class, but I was overriding the Meta attribute without properly subclassing it as described in the docs, and in the child class' Meta I had no ordering, so the value from the parent was lost.
Note that adding the - in ordering = ['-id'] orders your query in descending order.
I think the correct solution is actually not to specify a default ordering on the model because this can have severe side effects in the future for anything else that happens to use this model. An example of a side effect that might come back to haunt you is the query planner deciding to use the incorrect index to priortise ordering instead of filtering. This can have huge performance implications on larger tables. Instead you should specify the default ordering on your drf view or specifically in the author's case, the query you are returning should specify an order_by().
|
74

I was getting this warning when i used objects.all() in my view.py

profile_list = Profile.objects.all()
paginator = Paginator(profile_list, 25)

to fix this i changed my code to :

profile_list = Profile.objects.get_queryset().order_by('id')
paginator = Paginator(profile_list, 25)

1 Comment

I think it should be Profile.objects.all().order_by('id') .. at least that works for me, otherwise I get AttributeError: type object 'Profile' has no attribute 'get_queryset'
21

In my case, I had to add order_by('id') instead of ordering.

class IntakeCaseViewSet(viewsets.ModelViewSet):
    schema = None
    queryset = IntakeCase.objects.all().order_by('id')

Ordering needs to be in the model using Class Meta (not View).

Comments

11

Let me give an answer updated to new developments...

https://code.djangoproject.com/ticket/6089

The default ordering of the User model has been removed in Django. If you found yourself at this page because of an upgrade, it's very likely connected to this change.

There are 2 versions of this problem you might be dealing with.

  1. your own model does not have a default ordering in its Meta (see accepted answer)
  2. you are using a model from an app you are using as a dependency which does not have a default ordering

Since literally the Django User model itself does not adhere to ordering, it's very clear that the second scenario cannot be resolved by asking the maintainers of those dependencies to put in a default ordering. Okay, so now you either have to override the model being used for whatever your doing (sometimes a good idea, but not good for addressing such a minor issue).

So you're left with addressing it on the view level. You also want to do something that will play nicely with any ordering filter class you have applied. For that, set the view's ordering parameter.

class Reviewers(ListView):
    model = User
    paginate_by = 50
    ordering = ['username']

Also see Is there Django List View model sort?

Comments

5

Another option is to add OrderingFilter

http://www.django-rest-framework.org/api-guide/filtering/#orderingfilter

Comments

2

Including this didn't work for me.

class Meta:
   ordering = ['-id']

But changing get_queryset(self) and sorting the list with .order_by('id') did. Maybe worked because I'm using filters, I don't know

class MyView(viewsets.ModelViewSet):
    queryset = MyModel.objects.all()
    serializer_class = MySerializerSerializer

    def get_queryset(self):
        user = self.request.user
        return MyModel.objects.filter(owner=user).order_by('id')

Comments

1

Update the model meta class instead.

class UsefulModel(models.Model):
    
    class Meta:
        ordering='-created' # for example

you can still override the ordering from the view attribute 'ordering' As advised by AlanSE previously.

class UsefulView(ListView):
    ordering = ['-created']

Comments

0

In my case, it expected a tuple and that tuple has to contain a comma even when you are parsing just an item in it.

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.