diff options
| author | Tom Christie | 2015-01-22 17:25:12 +0000 | 
|---|---|---|
| committer | Tom Christie | 2015-01-22 17:25:12 +0000 | 
| commit | 43d983fae82ab23ca94f52deb29e938eb2a40e88 (patch) | |
| tree | 2466e74009d87b6698206b59f5b4681bcb6d66b0 /rest_framework | |
| parent | 0822c9e55820f8e4737329e38abc2e21718af9e5 (diff) | |
| download | django-rest-framework-43d983fae82ab23ca94f52deb29e938eb2a40e88.tar.bz2 | |
Add paging controls
Diffstat (limited to 'rest_framework')
3 files changed, 74 insertions, 16 deletions
| diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py index 1b4174bc..b3658aca 100644 --- a/rest_framework/pagination.py +++ b/rest_framework/pagination.py @@ -133,9 +133,14 @@ def _decode_cursor(encoded):      try:          querystring = b64decode(encoded.encode('ascii')).decode('ascii')          tokens = urlparse.parse_qs(querystring, keep_blank_values=True) -        offset = _positive_int(tokens['offset'][0]) -        reverse = bool(int(tokens['reverse'][0])) -        position = tokens.get('position', [None])[0] + +        offset = tokens.get('o', ['0'])[0] +        offset = _positive_int(offset) + +        reverse = tokens.get('r', ['0'])[0] +        reverse = bool(int(reverse)) + +        position = tokens.get('p', [None])[0]      except (TypeError, ValueError):          return None @@ -146,12 +151,13 @@ def _encode_cursor(cursor):      """      Given a Cursor instance, return an encoded string representation.      """ -    tokens = { -        'offset': str(cursor.offset), -        'reverse': '1' if cursor.reverse else '0', -    } +    tokens = {} +    if cursor.offset != 0: +        tokens['o'] = str(cursor.offset) +    if cursor.reverse: +        tokens['r'] = '1'      if cursor.position is not None: -        tokens['position'] = cursor.position +        tokens['p'] = cursor.position      querystring = urlparse.urlencode(tokens, doseq=True)      return b64encode(querystring.encode('ascii')).decode('ascii') @@ -430,10 +436,12 @@ class CursorPagination(BasePagination):      # Determine how/if True, False and None positions work - do the string      # encodings work with Django queryset filters?      # Consider a max offset cap. +    # Tidy up the `get_ordering` API (eg remove queryset from it)      cursor_query_param = 'cursor'      page_size = api_settings.PAGINATE_BY      invalid_cursor_message = _('Invalid cursor')      ordering = None +    template = 'rest_framework/pagination/previous_and_next.html'      def paginate_queryset(self, queryset, request, view=None):          self.base_url = request.build_absolute_uri() @@ -452,17 +460,22 @@ class CursorPagination(BasePagination):          # Cursor pagination always enforces an ordering.          if reverse: -            queryset = queryset.order_by(_reverse_ordering(self.ordering)) +            queryset = queryset.order_by(*_reverse_ordering(self.ordering))          else: -            queryset = queryset.order_by(self.ordering) +            queryset = queryset.order_by(*self.ordering)          # If we have a cursor with a fixed position then filter by that.          if current_position is not None: -            primary_ordering_attr = self.ordering[0].lstrip('-') -            if self.cursor.reverse: -                kwargs = {primary_ordering_attr + '__lt': current_position} +            order = self.ordering[0] +            is_reversed = order.startswith('-') +            order_attr = order.lstrip('-') + +            # Test for: (cursor reversed) XOR (queryset reversed) +            if self.cursor.reverse != is_reversed: +                kwargs = {order_attr + '__lt': current_position}              else: -                kwargs = {primary_ordering_attr + '__gt': current_position} +                kwargs = {order_attr + '__gt': current_position} +              queryset = queryset.filter(**kwargs)          # If we have an offset cursor then offset the entire page by that amount. @@ -501,6 +514,11 @@ class CursorPagination(BasePagination):              if self.has_previous:                  self.previous_position = current_position +        # Display page controls in the browsable API if there is more +        # than one page. +        if self.has_previous or self.has_next: +            self.display_page_controls = True +          return self.page      def get_next_link(self): @@ -642,5 +660,23 @@ class CursorPagination(BasePagination):          return tuple(ordering)      def _get_position_from_instance(self, instance, ordering): -        attr = getattr(instance, ordering[0]) +        attr = getattr(instance, ordering[0].lstrip('-'))          return six.text_type(attr) + +    def get_paginated_response(self, data): +        return Response(OrderedDict([ +            ('next', self.get_next_link()), +            ('previous', self.get_previous_link()), +            ('results', data) +        ])) + +    def get_html_context(self): +        return { +            'previous_url': self.get_previous_link(), +            'next_url': self.get_next_link() +        } + +    def to_html(self): +        template = loader.get_template(self.template) +        context = Context(self.get_html_context()) +        return template.render(context) diff --git a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css index 15b42178..04f12ed3 100644 --- a/rest_framework/static/rest_framework/css/bootstrap-tweaks.css +++ b/rest_framework/static/rest_framework/css/bootstrap-tweaks.css @@ -63,10 +63,20 @@ a single block in the template.  .pagination>.disabled>a,  .pagination>.disabled>a:hover,  .pagination>.disabled>a:focus { -  cursor: default; +  cursor: not-allowed;    pointer-events: none;  } +.pager>.disabled>a, +.pager>.disabled>a:hover, +.pager>.disabled>a:focus { +  pointer-events: none; +} + +.pager .next { +  margin-left: 10px; +} +  /*=== dabapps bootstrap styles ====*/  html { diff --git a/rest_framework/templates/rest_framework/pagination/previous_and_next.html b/rest_framework/templates/rest_framework/pagination/previous_and_next.html new file mode 100644 index 00000000..eacbfff4 --- /dev/null +++ b/rest_framework/templates/rest_framework/pagination/previous_and_next.html @@ -0,0 +1,12 @@ +<ul class="pager"> +{% if previous_url %} +    <li class="previous"><a href="{{ previous_url }}">« Previous</a></li> +{% else %} +    <li class="previous disabled"><a href="#">« Previous</a></li> +{% endif %} +{% if next_url %} +    <li class="next"><a href="{{ next_url }}">Next »</a></li> +{% else %} +    <li class="next disabled"><a href="#">Next »</li> +{% endif %} +</ul> | 
