Git Product home page Git Product logo

django-cursor-pagination's People

Contributors

cmmartti avatar drarok avatar dtkav avatar ifigotin avatar jaap3 avatar joealcorn avatar mjtamlyn avatar moggers87 avatar patrick91 avatar schubmannm avatar thommor avatar untidy-hair avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

django-cursor-pagination's Issues

Consider applying existing ordering of queryset, rather than requiring ordering as arg.

CursorPaginator takes 2 parameters queryset and ordering and applies the ordering to the queryset on init.

However, if a query set is already adequately ordered for pagination purposes, either explicitly or on the model's Meta class, it would be convenient to be able to infer and user that ordering and to call CursorPaginator with a single queryset argument.

For example:

class CursorPaginator(object):

    def __init__(self, queryset, ordering=None):
        self.queryset = queryset
        self.ordering = ordering

        if ordering:
            self.queryset = queryset.order_by(*ordering)
        elif queryset.query.order_by:
            self.ordering = queryset.query.order_by
        elif queryset.query.get_meta().ordering
            self.ordering =  queryset.query.get_meta().ordering
        else:
            raise InvalidCursor('No ordering supplied of inferable') 

Row Value Misused Error

The code that I'm using is very similar to the example provided in the README:

qs = Vendor.objects.filter(client__pk=client_id)
page_size = 2
paginator = CursorPaginator(qs, ordering=('-completed_date', '-id'))
page = paginator.page(first=page_size, after=after)
data = {
    'objects': [p for p in page],
    'has_next': True,
    'last_cursor': paginator.cursor(page[-1])
}
return data

This is working for the first request with the after argument defaulted to None, but as soon as the after argument is added, I receive the following error row value misused. This error is caused mainly by the line queryset = queryset.annotate(_cursor=Tuple(*[o.lstrip('-') for o in self.ordering])). When accessing the queryset variable after this line, I receive the error row value misused. I'm using Django 2.1.5 and sqlite3 2.6.0.

Support async queryset evaluation

The page() function evaluates querysets and will fail if called from within an async function. Will raise a SynchronousOnlyOperation exception.

Consider using the offset approach used by DRF

In this project at the moment I have not used the offset approach which used by Django rest framework and inspired by David Cramer's blog post. The main reason for this is that it doesn't provide "globally unique" cursors. In particular you cannot guarantee that before=FIRST_ELEMENT_CURSOR will give the correct data set, as the offset of the first element cannot be calculated. This edge case could possibly be covered by a second query checking for uniqueness on that value, although this would be overkill in most situations.

From a bit of research, Disqus is the only implementation of cursor pagination "in the wild" which makes use of offsets. Both twitter and facebook use id or timestamp based approaches. By encoding the value of all fields used in the ordering into the cursor (rather than just the first in DRF's implementation), we don't need offsets and the resulting code is much simpler - in particular when calculating the cursor for every item on the page.

I've always tried to make sure that any paginated set has a truly unique ordering - for most applications either id or created is sufficient. It also means you can do nice things like .order_by('-score', '-created') where score may not be unique enough to guarantee that you don't need large offsets.

Thoughts?

Migrate to flit, pdm, or hatch

Currently, our build step look like this:

python -m pip install build twine
python -m build
python -m twine check dist/*
python -m twine upload dist/*

I think we can migrate to a tool like flit, pdm or hatch, which won't require us to have a setup.py file (and we can migrate to pyproject.toml)

Consider using the offset approach used by DRF

In this project at the moment I have not used the offset approach which used by Django rest framework and inspired by David Cramer's blog post. The main reason for this is that it doesn't provide "globally unique" cursors. In particular you cannot guarantee that before=FIRST_ELEMENT_CURSOR will give the correct data set, as the offset of the first element cannot be calculated. This edge case could possibly be covered by a second query checking for uniqueness on that value, although this would be overkill in most situations.

From a bit of research, Disqus is the only implementation of cursor pagination "in the wild" which makes use of offsets. Both twitter and facebook use id or timestamp based approaches. By encoding the value of all fields used in the ordering into the cursor (rather than just the first in DRF's implementation), we don't need offsets and the resulting code is much simpler - in particular when calculating the cursor for every item on the page.

I've always tried to make sure that any paginated set has a truly unique ordering - for most applications either id or created is sufficient. It also means you can do nice things like .order_by('-score', '-created') where score may not be unique enough to guarantee that you don't need large offsets.

Thoughts?

Pagination doesn't work with mongoengine

Since mongoengine doesn't support django annotate, the following exception occurs when trying to paginate.
'QuerySet' object has no attribute 'annotate'
in the following line:
queryset = queryset.annotate(_cursor=Tuple(*[o.lstrip('-') for o in self.ordering]))

Using OrderBy objects does not work

Since the direction of the sorts are checked assuming they are strings (source), attempting to use an OrderBy object will cause a crash.

The use of an OrderBy object is required in order to change how null values are sorted (source)

qs.order_by(F('field_name').desc(nulls_last=True))

Pagination with null values

๐Ÿ‘‹ First of all, thank you for this awesome library!

I encountered this None/Null issue at my workplace (modified) where the last object of the previous page has NULL value for one of the ordering keys.

ERROR:  invalid input syntax for type smallint: "None" at character 1564

... AND ("product"."tier" > 'None' OR ("product"."tier" = 'None' AND "product"."id" > '555'))) ORDER BY "product"."tier" ASC, "product"."id" DESC LIMIT 101

I made a patch and that is working for me but now I am trying to update this library, so I can use it as a regular library.

This is what I have so far: https://gist.github.com/untidy-hair/9e432cb024c00dd8fa25ebcceda6a81a
And this is the new test: https://gist.github.com/untidy-hair/563c9efa55f14108fbb2199d68534cc5

Whether Null comes at the beginning or at the end of the order is DB dependent. In that sense, my implementation there is a little bit opinionated for always putting NULL at the end (except when "last" exists). I personally cannot think of any reasons why NULL should come first but that can be configurable, too, if we like. (Caveat on README should be updated with this)

I was going to make a PR but first I thought it might be better asking here! Thanks in advance!

[EDIT]
On a second thought, maybe it's easier to be able to see diffs than just the whole Gist file so I'm creating this PR as a draft for discussions ๐Ÿ™ #45

Consider using the offset approach used by DRF

In this project at the moment I have not used the offset approach which used by Django rest framework and inspired by David Cramer's blog post. The main reason for this is that it doesn't provide "globally unique" cursors. In particular you cannot guarantee that before=FIRST_ELEMENT_CURSOR will give the correct data set, as the offset of the first element cannot be calculated. This edge case could possibly be covered by a second query checking for uniqueness on that value, although this would be overkill in most situations.

From a bit of research, Disqus is the only implementation of cursor pagination "in the wild" which makes use of offsets. Both twitter and facebook use id or timestamp based approaches. By encoding the value of all fields used in the ordering into the cursor (rather than just the first in DRF's implementation), we don't need offsets and the resulting code is much simpler - in particular when calculating the cursor for every item on the page.

I've always tried to make sure that any paginated set has a truly unique ordering - for most applications either id or created is sufficient. It also means you can do nice things like .order_by('-score', '-created') where score may not be unique enough to guarantee that you don't need large offsets.

Thoughts?

Fails to handle multliple ordering fields properly

We need to ensure that only the last field is __lt or __gt, the previous ones should be __lte.

As an example, order on ('a', 'b'), page at 2.

Data:

 a | b 
 1 | 2
 1 | 3
 1 | 4
 2 | 1

"second" page will return only the last element.

[bug] Orderings that span relationships are not handled.

e.g.

  File "/Users/adam/coding/photocrowd/api/cursor.py", line 15, in connection_from_cursor_paginated
    edge = edge_type(node=item, cursor=paginator.cursor(item))
  File "/Users/adam/.virtualenvs/photocrowd/lib/python2.7/site-packages/cursor_pagination.py", line 119, in cursor
    return self.encode_cursor(self.position_from_instance(instance))
  File "/Users/adam/.virtualenvs/photocrowd/lib/python2.7/site-packages/cursor_pagination.py", line 114, in position_from_instance
    attr = getattr(instance, order.lstrip('-'))
AttributeError: 'Submission' object has no attribute 'judge_feedback__placing'

line 114 in cursor_paginator.py

Solution:
Split ordering on __ and recursively getattr

Consider using the offset approach used by DRF

In this project at the moment I have not used the offset approach which used by Django rest framework and inspired by David Cramer's blog post. The main reason for this is that it doesn't provide "globally unique" cursors. In particular you cannot guarantee that before=FIRST_ELEMENT_CURSOR will give the correct data set, as the offset of the first element cannot be calculated. This edge case could possibly be covered by a second query checking for uniqueness on that value, although this would be overkill in most situations.

From a bit of research, Disqus is the only implementation of cursor pagination "in the wild" which makes use of offsets. Both twitter and facebook use id or timestamp based approaches. By encoding the value of all fields used in the ordering into the cursor (rather than just the first in DRF's implementation), we don't need offsets and the resulting code is much simpler - in particular when calculating the cursor for every item on the page.

I've always tried to make sure that any paginated set has a truly unique ordering - for most applications either id or created is sufficient. It also means you can do nice things like .order_by('-score', '-created') where score may not be unique enough to guarantee that you don't need large offsets.

Thoughts?

Major Issue: Breaks on Django 3.1.7

Hello,
This package is broken on Django version 3.1.7. Which is at least the latest stable release of Django.

Example code:

from cursor_pagination import CursorPaginator

Result:

"ImportError: cannot import name 'six' from 'django.utils'"

Expected Result:

CursorPaginator imported successfully.

Consider using the offset approach used by DRF

In this project at the moment I have not used the offset approach which used by Django rest framework and inspired by David Cramer's blog post. The main reason for this is that it doesn't provide "globally unique" cursors. In particular you cannot guarantee that before=FIRST_ELEMENT_CURSOR will give the correct data set, as the offset of the first element cannot be calculated. This edge case could possibly be covered by a second query checking for uniqueness on that value, although this would be overkill in most situations.

From a bit of research, Disqus is the only implementation of cursor pagination "in the wild" which makes use of offsets. Both twitter and facebook use id or timestamp based approaches. By encoding the value of all fields used in the ordering into the cursor (rather than just the first in DRF's implementation), we don't need offsets and the resulting code is much simpler - in particular when calculating the cursor for every item on the page.

I've always tried to make sure that any paginated set has a truly unique ordering - for most applications either id or created is sufficient. It also means you can do nice things like .order_by('-score', '-created') where score may not be unique enough to guarantee that you don't need large offsets.

Thoughts?

Consider providing a REST framework pagination class for this package.

Based on our conversation about this, I think your simpler implementation may well be a better approach to cursor pagination that the default provided by REST framework. It'd certainly be valuable to have this scheme easily available to REST framework users.

Any thoughts on if you'd consider a REST framework pagination class for this package?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.