drf-haystack icon indicating copy to clipboard operation
drf-haystack copied to clipboard

get_queryset() error with Django pagination

Open isaacjoy opened this issue 6 years ago • 4 comments

Hi there,

I'm using Haystack with the DRF and I noticed that when I added the get_serializer_class to my Haystack viewset, the pagination returned {"detail": "Invalid page."} for every page other than 1.

I want to return a custom serializer dependent on the user's authentication.

Here is my viewset:

class SearchViewSet(HaystackViewSet):
    throttle_classes = (SearchThrottle,)
    permission_classes = (AllowAny,)
    
    def get_serializer_class(self):
        if self.request.user.is_anonymous:
            return PublicSearchSerializer
        else:
            return SearchSerializer

I saw that in the DRF for pagination they have this method to paginate a queryset..

    invalid_page_message = _('Invalid page.')

    def paginate_queryset(self, queryset, request, view=None):
        """
        Paginate a queryset if required, either returning a
        page object, or `None` if pagination is not configured for this view.
        """
        page_size = self.get_page_size(request)
        if not page_size:
            return None

        paginator = self.django_paginator_class(queryset, page_size)
        page_number = request.query_params.get(self.page_query_param, 1)
        if page_number in self.last_page_strings:
            page_number = paginator.num_pages

        try:
            self.page = paginator.page(page_number)
        except InvalidPage as exc:
            msg = self.invalid_page_message.format(
                page_number=page_number, message=six.text_type(exc)
            )
            raise NotFound(msg)

        if paginator.num_pages > 1 and self.template is not None:
            # The browsable API should display pagination controls.
            self.display_page_controls = True

        self.request = request
        return list(self.page)

so self.page = paginator.page(page_number) is failing and that relies on the core paginator from Django which I guess it not at fault?

I saw that Haystack's get_queryset() in HaystackGenericAPIView looks like this:

    def get_queryset(self, index_models=[]):
        """
        Get the list of items for this view.
        Returns ``self.queryset`` if defined and is a ``self.object_class``
        instance.
        @:param index_models: override `self.index_models`
        """
        if self.queryset is not None and isinstance(self.queryset, self.object_class):
            queryset = self.queryset.all()
        else:
            queryset = self.object_class()._clone()
            if len(index_models):
                queryset = queryset.models(*index_models)
            elif len(self.index_models):
                queryset = queryset.models(*self.index_models)
        return queryset

Could it be that this is not correctly passing the queryset to the pagination?

I'm using the following Django paginator class in my settings:

        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
        'PAGE_SIZE': int(os.getenv('DJANGO_PAGINATION_LIMIT', 10)),

Apologies if this was long-winded - first time creating an issue.

isaacjoy avatar Apr 02 '18 17:04 isaacjoy

Hey, thanks for the detailed error report. I'll have a look!

rhblind avatar Apr 02 '18 19:04 rhblind

It seems this is an issue within Haystack itself: https://github.com/django-haystack/django-haystack/issues/1586

isaacjoy avatar Apr 03 '18 08:04 isaacjoy

Oddly I also get this is if I add ?page=1 to the query string but not if I leave the query string empty.

bentappin avatar Jul 02 '18 08:07 bentappin

I thought I'd report back what my problem was as I think it's probably the same as this issue.

Anything in the query string is being used in the FilterQueryBuilder including the page parameter to filter the queryset.

Adding exclude = ['page'] to my serializer has fixed this in the short term for me.

bentappin avatar Jul 02 '18 11:07 bentappin