toastdriven / restless

A lightweight REST miniframework for Python.
http://restless.readthedocs.org/en/latest/
BSD 3-Clause "New" or "Revised" License
831 stars 107 forks source link

Support pagination natively #78

Open seocam opened 8 years ago

seocam commented 8 years ago

What do you guys think about native support to pagination? We could implement that using Django paginator (for example) since it doesn't have external dependencies.

That's helpful in most listing APIs. Here is just an example on how it could be implemented in the Resource:

    DEFAULT_RESULTS_PER_PAGE = 20

class Resource(object):

   ...

    def serialize_list(self, data):
        if getattr(self, 'paginate', False):
            page_size = getattr(self, 'page_size', DEFAULT_RESULTS_PER_PAGE)
            paginator = Paginator(data, page_size)

            try:
                page_number = int(self.request.GET.get('p', 1))
            except ValueError:
                page_number = None

            if page_number not in paginator.page_range:
                raise BadRequest('Invalid page number')

            self.page = paginator.page(page_number)
            data = self.page.object_list

        return super().serialize_list(data)

    def wrap_list_response(self, data):
        response_dict = super().wrap_list_response(data)

        if hasattr(self, 'page'):
            if self.page.has_next():
                next_page = self.page.next_page_number()
            else:
                next_page = None

            if self.page.has_previous():
                previous_page = self.page.previous_page_number()
            else:
                previous_page = None

            response_dict['pagination'] = {
                'num_pages': self.page.paginator.num_pages,
                'count': self.page.paginator.count,
                'page': self.page.number,
                'start_index': self.page.start_index(),
                'end_index': self.page.end_index(),
                'next_page': next_page,
                'previous_page': previous_page,
            }

        return response_dict

And to use simply add to your resource:

MyResource(Resource):
    paginate = True
    page_size = 50  # optional

If that seems useful I'm willing to create a PR with tests and docs.

toxinu commented 8 years ago

I think it's better to write your own paginate mixin. Restless is not Django-only and it could be tricky to support pagination for every usage.

ghost commented 7 years ago

@seocam nice approach, just a note: there's not need to use

        try:
            page_number = int(self.request.GET.get('p', 1))
        except ValueError:
            page_number = None

        if page_number not in paginator.page_range:
            raise BadRequest('Invalid page number')

since the django Paginator class will take care of bad page numbers, so your code can be even shorter:

def serialize_list(self, data):
    if getattr(self, 'paginate', False):
        page_size = getattr(self, 'page_size', DEFAULT_RESULTS_PER_PAGE)
        paginator = Paginator(data, page_size)
        page_number = self.request.GET.get('p', 1)
        self.page = paginator.page(page_number)  #  This django method takes care of the page number
        data = self.page.object_list

    return super().serialize_list(data)

https://github.com/django/django/blob/master/django/core/paginator.py#L52

repodevs commented 7 years ago

Just add a little note:

def serialize_list(self, data):
    if getattr(self, 'paginate', False):
        page_size = getattr(self, 'page_size', DEFAULT_RESULTS_PER_PAGE)
        paginator = Paginator(data.value, page_size) # get value data
        page_number = self.request.GET.get('p', 1)
        self.page = paginator.page(page_number)  #  This django method takes care of the page number
        data = self.page.object_list

    return super().serialize_list(data)

https://docs.djangoproject.com/en/1.10/topics/pagination/#example

If you got error *** TypeError: object of type 'Data' has no len() that my snippet code fix that error

CalebeGeazi commented 6 years ago

@seocam just wondering if you had any plans on submitting a PR for this feature? Thanks!

seocam commented 6 years ago

Hey @CalebeGeazi. Honestly I had forgot that this issue was open. Thanks for bumping it up.

@toastdriven do you have any thoughts about that? If you don't oppose I think it's time to add a few extra features here ;)

seocam commented 5 years ago

I've merged #114 witch adds pagination for Django. @Marcelo-Theodoro could you create a new PR that actually adds the pagination independent of framework?

Thanks!

Marcelo-Theodoro commented 5 years ago

Guys, do you think that the pagination should be enabled or disabled by default?

cuducos commented 5 years ago

Guys, do you think that the pagination should be enabled or disabled by default?

I think we could turn it on or off depending on the per_page class attribute. If it getattr(resource, 'per_page') evaluates to True, pagination is enabled, if it evaluates do False, it's disabled.