Cursor pagination doesn't work when attempting to paginate on a multiple order by values when the first value is non-unique. #7888
-
Hey, I appreciate I'm sidestepping the discussion phase, but I think this is a fairly clear bug (and there's no test coverage for it). The This is an example of where it fails:
The cursor created by the paginator will create a query like this I've written a fix for this, where I create a compound cursor with all of the ordering parameters plus the primary key. I then do something like a tuple comparison between the cursor and the table (using some Q objects). It works fine, and this test passes with the fix. Let me know if you'd like the patch. |
Beta Was this translation helpful? Give feedback.
Replies: 6 comments 10 replies
-
If there are any changes to be made here I'd expect them to be around better highlighting aspects of the documentation rather than a required fix. I think we're fairly clear about the uniqueness requirement with... "Cursor based pagination requires that there is a unique, unchanging ordering of items in the result set." and "Proper use of cursor based pagination requires a little attention to detail. You'll need to think about what ordering you want the scheme to be applied against. [...] Should be unique, or nearly unique. Millisecond precision timestamps are a good example." I don't fully recall the details of our "position-plus-offset implementation, style that allows it to properly support not-strictly-unique values as the ordering.", but I'd expect any changes in this space to provide a clear example with a case where cursor+offset isn't sufficient. (Also, using a composite key including the PK may sometimes be suitable, but not neccessarily, since the composite of the two might not neccessarily result in a nicely indexed SQL query.) |
Beta Was this translation helpful? Give feedback.
-
Right, but then the typical solution is that you order by https://www.django-rest-framework.org/api-guide/filtering/#orderingfilter However, this doesn't work reliably with the CursorPaginator. This class makes an assumption about ordering that does not work in a large number of cases. It only uses the first parameter (note
And like I said, the query that you would use to support multiple parameters is very simple, and does not result in any additional computation because the queryset has already been sorted (but not limited) by those parameters. If this is the correct behaviour. There should be an additional constraint listed here:
Perhaps you could add to that list:
In response to that specific comment, I double checked my work. I added tests, and I don't think it does. $ pytest tests/test_cursor_pagination.py -v tests/test_cursor_pagination.py::test_filtered_items_are_paginated[page_size_divisor_of_offset] FAILED [ 20%] Again, it's pretty simple to get working with a query that adds no additional complexity :) |
Beta Was this translation helpful? Give feedback.
-
@sonthonaxrk great post! we've hit the same issue. Would you mind sharing your compound cursor solution? Is there a reason why this it not getting implemented in DRF? |
Beta Was this translation helpful? Give feedback.
-
I do have the same exact problem, and I think maintainers should really work on this instead of blaming not understanding documentation. |
Beta Was this translation helpful? Give feedback.
-
@Hassan-Elseoudy @rollue @amegianeg I've got better things to do than write a package that just fixes a bug. It's a waste of time to do so because the package will be obsolete as soon as the bug is fixed. I think your best best if you want this fixed is to just copy my new pagination.py file into your project. Then wait a few months and raise the issue yourself because I've obviously annoyed the DRF maintainers too much (which is fair enough, no one likes being told their wrong for free). |
Beta Was this translation helpful? Give feedback.
-
I have a situation where the user can decide the ordering (dynamic), but I always add the I then thought that I better make sure the CursorPaginator handles multiple ordering fields, so I stepped though the code and discovered it only used the first ordering field! I then searched for @sonthonaxrk Thank you for your time on providing a patch and creating this discussion. |
Beta Was this translation helpful? Give feedback.
@Hassan-Elseoudy @rollue @amegianeg I've got better things to do than write a package that just fixes a bug. It's a waste of time to do so because the package will be obsolete as soon as the bug is fixed.
I think your best best if you want this fixed is to just copy my new pagination.py file into your project.
Then wait a few months and raise the issue yourself because I've obviously annoyed the DRF maintainers too much (which is fair enough, no one likes being told their wrong for free).