Skip to content

Instantly share code, notes, and snippets.

@keturn
Last active April 21, 2022 15:21

Revisions

  1. keturn revised this gist May 22, 2017. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions test_cursorpagination.py
    Original file line number Diff line number Diff line change
    @@ -8,9 +8,8 @@ class FloatyModel(models.Model):


    class FloatyViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = FloatyModel.objects.all()
    queryset = FloatyModel.objects.all().order_by('-score')
    pagination_class = CursorPagination
    ordering = '-score'
    page_size = 3


  2. keturn revised this gist May 22, 2017. 1 changed file with 21 additions and 0 deletions.
    21 changes: 21 additions & 0 deletions fpcursorpagination.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    class FloatingPointCursorPagination(CursorPagination):
    __rounding_down = decimal.Context(prec=14, rounding=decimal.ROUND_FLOOR)
    __rounding_up = decimal.Context(prec=14, rounding=decimal.ROUND_CEILING)

    def _get_position_from_instance(self, instance, ordering):
    field_name = ordering[0].lstrip('-')
    if isinstance(instance, dict):
    attr = instance[field_name]
    else:
    attr = getattr(instance, field_name)

    if isinstance(attr, float):
    # repr gives more precision than str
    # but we still lost some precision just from the postgresql-to-python translation.
    if ordering[0][0] == '-':
    attr = self.__rounding_down.create_decimal_from_float(attr)
    else:
    attr = self.__rounding_up.create_decimal_from_float(attr)

    attr_str = force_text(attr)
    return attr_str
  3. keturn created this gist May 22, 2017.
    32 changes: 32 additions & 0 deletions test_cursorpagination.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,32 @@
    from django.db import models
    from rest_framework import viewsets
    from rest_framework.pagination import CursorPagination


    class FloatyModel(models.Model):
    score = models.FloatField()


    class FloatyViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = FloatyModel.objects.all()
    pagination_class = CursorPagination
    ordering = '-score'
    page_size = 3


    class TestFloatingPointCursorPagination(APITestCase):
    def test_page_boundary_does_not_repeat_elements(self, test_data):
    for i in range(12):
    FloatyModel.objects.create(score=i/9.0)

    viewset_url = 'FIXME'
    first_response = self.client.get(viewset_url)

    first_page_last_item = first_response.data['results'][-1]

    second_response = self.client.get(first_response['next'])

    second_page_first_item = second_response.data['results'][0]

    self.assertNotEqual(first_page_last_item['pk'], second_page_first_item['pk'])
    self.assertGreater(first_page_last_item['score'], second_page_first_item['score'])