django-oscar / django-oscar-api

RESTful JSON API for django-oscar
Other
369 stars 161 forks source link

Handling cursorpagination: default ordering field #163

Closed rollue closed 5 years ago

rollue commented 5 years ago

I'm trying to use this library for our new service that need ecommerce-related backend APIs.

I have DRF's CursorPagination as our default pagination class. This orders queryset with created field, assuming that there must be a 'created' timestamp field on the model instances.

Django-oscar, however, seems to have date_created as the default timestamp field.

This means either of our previously-developed apps, or the django-oscar related apps should change the timestamp field name. Then change the CursorPagination's ordering field to the unified timestamp field, which should either be date_created or created.

Am I getting this right? Or is there any other way?

Changing the fields names on our previous work, or forking django-oscar just for this seem too laborious.

Thanks.

specialunderwear commented 5 years ago

This is not the place to ask for support, especially not support on django restframework. Please try using LimitOffsetPagination

maerteijn commented 5 years ago

The docs of DRF clearly explain how you can specify the ordering field: https://www.django-rest-framework.org/api-guide/pagination/#details-and-limitations

This is the way how to do it.

rollue commented 5 years ago

@specialunderwear @maerteijn Thanks. I'll be more careful with questions next time :)

rollue commented 5 years ago

This is for anyone trying to use CursorPagination with oscar-api

  1. DRF's custom cursor pagination's ordering field is created by default.
  2. Most of the models in django-oscar do not have created as model field. Most of them have either date_created or not have any timestamp-related field at all(ex. Country Model, ProductClass Model etc.)
  3. In this case, you have to override DRF's CursorPagination class to dynamically change ordering field depending on the model field.
  4. For my case, I have managed to handle cursor pagination with the following code. It basically tries to use the following fields for ordering in order:
    1. created - DRF CursorPagination's default setting
    2. date_created- most of the models in django-oscar uses this instead of created
    3. id - django's default is incremental field, meaning it can be used for ordering
    4. if none of the above matches, then tries to look for the ordering field defined in model.Meta (ex. AbstractCountryModel)

I include my working code hoping it might help someone. Good luck.

from django.core.exceptions import FieldError
from rest_framework.pagination import CursorPagination

class CustomCursorPagination(CursorPagination):
    def paginate_queryset(self, queryset, request, view=None):
        """
        In oscar/api/basket endpoint, the queryset type is not django's default queryset: 
        it is QuerySetList defined in oscarapi.views.utils.QuerysetList. QuerySetList has 
        queryset field, so queryset.queryset is passed as parameter.
        """

        if not hasattr(queryset.model, "created"):
            self.ordering = ["-date_created"]
        try:
            return super().paginate_queryset(queryset, request, view=None)
        except AttributeError:
            queryset = queryset.queryset  # for oscarapi/basket/
        except FieldError:
            if hasattr(queryset.model, "id"):
                self.ordering = ["-id"]
            else:
                self.ordering = queryset.model.Meta.ordering  # for oscarapi/coutries/
        return super().paginate_queryset(queryset, request, view=None)