philipn/django-rest-framework-filters

DRF return empty list in results randomly, but count still correct

yothinix opened this issue · 4 comments

When I'm trying to use the custom filter it's working all the time in local development, but when we deploy to Production (Docker Swarm) We found an issue that sometimes the API response is returned empty results randomly, but the count is correct. Below is the example results from the API

API Response

{
    'count': 1, 
    'next': 'http://localhost:8000/api/somethings/?email=test%40example.com&limit=0&offset=0', 
    'previous': None, 
    'results': []
}

Right now we need to restart a uWSGI service (By restarting docker swarm for this service) and the problem is fixed for a moment and randomly happen again.

Here is our DRF view

class SomeView(ListCreateAPIView):
    queryset = SomeModel.objects.all()
    serializer_class = SomeModelSerializer
    filter_backends = (OrderingFilter, DjangoFilterBackend)
    filter_class = CustomFilter
    ordering = ('id',)

    def list(self, request, *args, **kwargs):
        if request.GET.get('all', None):
            # Do something
            serializer = self.get_serializer(queryset, many=True)
            return Response(serializer.data)
        else:
            return super(SomeView, self).list(self, request, *args, **kwargs)

Here is our CustomFilter

from django_filters.rest_framework.filters import CharFilter
import rest_framework_filters as filters


class CustomFilter(filters.FilterSet):
    json_field_separator = '___'

    json_field_is = CharFilter(name='json_field', method='filter_json_field')
    json_field_is_not = CharFilter(name='json_field', method='exclude_json_field')

    def split_lookup_field(self, value):
        return dict(field.split(':') for field in value.split(self.json_field_separator))

    def filter_json_field(self, queryset, name, value):
        try:
            lookup_field = self.split_lookup_field(value)
            return queryset.filter(**lookup_field)
        except (ValueError, FieldError):
            return queryset.none()

    def exclude_json_field(self, queryset, name, value):
        try:
            lookup_field = self.split_lookup_field(value)
        except (ValueError, FieldError):
            return queryset.none()

        for query_arg, query_value in lookup_field.items():
            queryset = queryset.exclude(**{query_arg: query_value})

        return queryset

    class Meta:
        model = SomeModel
        exclude = ['image', 'json_field']

Here is a version of the Package we use for this project

Django==1.10.8
djangorestframework==3.6.4
django-filter==1.0.4
djangorestframework-filters==0.10.2

We suspect that this is possible related to djangorestframework-filters since the only change before this error occured is when we implement CustomFilter above, Any idea?

UPDATE: The problem seems to be reduce, but still occured once we using DjangoFilterBackend from rest_framework_filters.backends package instead of django_filters

I believe this is related to #134 (comment). Please upgrade to django-filter 1.1.0, and let me know if that fixes your issue.

@rpkilby We just update django-filter to 1.1.0 and the problem still happen and seems to be more than before we upgrade. Is it possible that when API received load of requests would cause this problem?

It doesn't look like you're using any features specific to this package. I would see if you can reproduce this with just django-filter. e.g., inherit from django_filters.FilterSet and use the django_filters.rest_framework.DjangoFilterBackend.

@rpkilby Sorry for missing for 3 days, but we want to verify the results. It looks like the problem is not related to this package at all as you suggest. It look like that when we override pagination limit the pagination value spread across the request which is causing the problem above and I can reproduce it via unit test.

class SomeView(ListCreateAPIView):
    queryset = SomeModel.objects.all()
    serializer_class = SomeSerializer
    filter_backends = (OrderingFilter, DjangoFilterBackend)
    filter_class = CustomFilter
    ordering = ('id',)

    def list(self, request, *args, **kwargs):
        if request.GET.get('all', None):
            queryset = self.filter_queryset(self.get_queryset())
            self.pagination_class.default_limit = queryset.count()  # The problem is this line

            page = self.paginate_queryset(queryset)
            if page is not None:
                serializer = self.get_serializer(page, many=True)
                return self.get_paginated_response(serializer.data)

            serializer = self.get_serializer(queryset, many=True)

            return Response(serializer.data)
        else:
            return super(SomeView, self).list(self, request, *args, **kwargs)

I may close this issue now since it now solved, Thanks for helping.