text: import datetime
  from decimal import Decimal
  
  from django.test import TestCase
  from django.test.utils import override_settings
  from django.urls import path, reverse
  from django.utils.dateparse import parse_date
  from rest_framework import generics, serializers, status
  from rest_framework.test import APIRequestFactory
  
  from django_filters import filters
  from django_filters.rest_framework import DjangoFilterBackend, FilterSet
  
  from .models import (
      BaseFilterableItem,
      BasicModel,
      DjangoFilterOrderingModel,
      FilterableItem
  )
  
  factory = APIRequestFactory()
  
  
  class FilterableItemSerializer(serializers.ModelSerializer):
      class Meta:
          model = FilterableItem
          fields = '__all__'
  
  
  # Basic filter on a list view.
  class FilterFieldsRootView(generics.ListCreateAPIView):
      queryset = FilterableItem.objects.all()
      serializer_class = FilterableItemSerializer
      filterset_fields = ['decimal', 'date']
      filter_backends = (DjangoFilterBackend,)
  
  
  # These class are used to test a filter class.
  class SeveralFieldsFilter(FilterSet):
      text = filters.CharFilter(lookup_expr='icontains')
      decimal = filters.NumberFilter(lookup_expr='lt')
      date = filters.DateFilter(lookup_expr='gt')
  
      class Meta:
          model = FilterableItem
          fields = ['text', 'decimal', 'date']
  
  
  class FilterClassRootView(generics.ListCreateAPIView):
      queryset = FilterableItem.objects.all()
      serializer_class = FilterableItemSerializer
      filterset_class = SeveralFieldsFilter
      filter_backends = (DjangoFilterBackend,)
  
  
  # These classes are used to test a misconfigured filter class.
  class MisconfiguredFilter(FilterSet):
      text = filters.CharFilter(lookup_expr='icontains')
  
      class Meta:
          model = BasicModel
          fields = ['text']
  
  
  class IncorrectlyConfiguredRootView(generics.ListCreateAPIView):
      queryset = FilterableItem.objects.all()
      serializer_class = FilterableItemSerializer
      filterset_class = MisconfiguredFilter
      filter_backends = (DjangoFilterBackend,)
  
  
  class FilterClassDetailView(generics.RetrieveAPIView):
      queryset = FilterableItem.objects.all()
      serializer_class = FilterableItemSerializer
      filterset_class = SeveralFieldsFilter
      filter_backends = (DjangoFilterBackend,)
  
  
  # These classes are used to test base model filter support
  class BaseFilterableItemFilter(FilterSet):
      text = filters.CharFilter()
  
      class Meta:
          model = BaseFilterableItem
          fields = '__all__'
  
  
  class BaseFilterableItemFilterRootView(generics.ListCreateAPIView):
      queryset = FilterableItem.objects.all()
      serializer_class = FilterableItemSerializer
      filterset_class = BaseFilterableItemFilter
      filter_backends = (DjangoFilterBackend,)
  
  
  # Regression test for #814
  class FilterFieldsQuerysetView(generics.ListCreateAPIView):
      queryset = FilterableItem.objects.all()
      serializer_class = FilterableItemSerializer
      filterset_fields = ['decimal', 'date']
      filter_backends = (DjangoFilterBackend,)
  
  
  class GetQuerysetView(generics.ListCreateAPIView):
      serializer_class = FilterableItemSerializer
      filterset_class = SeveralFieldsFilter
      filter_backends = (DjangoFilterBackend,)
  
      def get_queryset(self):
          return FilterableItem.objects.all()
  
  
  urlpatterns = [
      path('<int:pk>/', FilterClassDetailView.as_view(), name='detail-view'),
      path('', FilterClassRootView.as_view(), name='root-view'),
      path('get-queryset/', GetQuerysetView.as_view(), name='get-queryset-view'),
  ]
  
  
  class CommonFilteringTestCase(TestCase):
      def _serialize_object(self, obj):
          return {'id': obj.id, 'text': obj.text, 'decimal': str(obj.decimal), 'date': obj.date.isoformat()}
  
      def setUp(self):
          """
          Create 10 FilterableItem instances.
          """
          base_data = ('a', Decimal('0.25'), datetime.date(2012, 10, 8))
          for i in range(10):
              text = chr(i + ord(base_data[0])) * 3  # Produces string 'aaa', 'bbb', etc.
              decimal = base_data[1] + i
              date = base_data[2] - datetime.timedelta(days=i * 2)
              FilterableItem(text=text, decimal=decimal, date=date).save()
  
          self.objects = FilterableItem.objects
          self.data = [
              self._serialize_object(obj)
              for obj in self.objects.all()
          ]
  
  
  class IntegrationTestFiltering(CommonFilteringTestCase):
      """
      Integration tests for filtered list views.
      """
  
      def test_get_filtered_fields_root_view(self):
          """
          GET requests to paginated ListCreateAPIView should return paginated results.
          """
          view = FilterFieldsRootView.as_view()
  
          # Basic test with no filter.
          request = factory.get('/')
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          self.assertEqual(response.data, self.data)
  
          # Tests that the decimal filter works.
          search_decimal = Decimal('2.25')
          request = factory.get('/', {'decimal': '%s' % search_decimal})
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          expected_data = [f for f in self.data if Decimal(f['decimal']) == search_decimal]
          self.assertEqual(response.data, expected_data)
  
          # Tests that the date filter works.
          search_date = datetime.date(2012, 9, 22)
          request = factory.get('/', {'date': '%s' % search_date})  # search_date str: '2012-09-22'
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          expected_data = [f for f in self.data if parse_date(f['date']) == search_date]
          self.assertEqual(response.data, expected_data)
  
      def test_filter_with_queryset(self):
          """
          Regression test for #814.
          """
          view = FilterFieldsQuerysetView.as_view()
  
          # Tests that the decimal filter works.
          search_decimal = Decimal('2.25')
          request = factory.get('/', {'decimal': '%s' % search_decimal})
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          expected_data = [f for f in self.data if Decimal(f['decimal']) == search_decimal]
          self.assertEqual(response.data, expected_data)
  
      def test_filter_with_get_queryset_only(self):
          """
          Regression test for #834.
          """
          view = GetQuerysetView.as_view()
          request = factory.get('/get-queryset/')
          view(request).render()
          # Used to raise "issubclass() arg 2 must be a class or tuple of classes"
          # here when neither `model' nor `queryset' was specified.
  
      def test_get_filtered_class_root_view(self):
          """
          GET requests to filtered ListCreateAPIView that have a filterset_class set
          should return filtered results.
          """
          view = FilterClassRootView.as_view()
  
          # Basic test with no filter.
          request = factory.get('/')
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          self.assertEqual(response.data, self.data)
  
          # Tests that the decimal filter set with 'lt' in the filter class works.
          search_decimal = Decimal('4.25')
          request = factory.get('/', {'decimal': '%s' % search_decimal})
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          expected_data = [f for f in self.data if Decimal(f['decimal']) < search_decimal]
          self.assertEqual(response.data, expected_data)
  
          # Tests that the date filter set with 'gt' in the filter class works.
          search_date = datetime.date(2012, 10, 2)
          request = factory.get('/', {'date': '%s' % search_date})  # search_date str: '2012-10-02'
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          expected_data = [f for f in self.data if parse_date(f['date']) > search_date]
          self.assertEqual(response.data, expected_data)
  
          # Tests that the text filter set with 'icontains' in the filter class works.
          search_text = 'ff'
          request = factory.get('/', {'text': '%s' % search_text})
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          expected_data = [f for f in self.data if search_text in f['text'].lower()]
          self.assertEqual(response.data, expected_data)
  
          # Tests that multiple filters works.
          search_decimal = Decimal('5.25')
          search_date = datetime.date(2012, 10, 2)
          request = factory.get('/', {
              'decimal': '%s' % (search_decimal,),
              'date': '%s' % (search_date,)
          })
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          expected_data = [f for f in self.data if parse_date(f['date']) > search_date and
                           Decimal(f['decimal']) < search_decimal]
          self.assertEqual(response.data, expected_data)
  
      def test_incorrectly_configured_filter(self):
          """
          An error should be displayed when the filter class is misconfigured.
          """
          view = IncorrectlyConfiguredRootView.as_view()
  
          request = factory.get('/')
          self.assertRaises(AssertionError, view, request)
  
      def test_base_model_filter(self):
          """
          The `get_filterset_class` model checks should allow base model filters.
          """
          view = BaseFilterableItemFilterRootView.as_view()
  
          request = factory.get('/?text=aaa')
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          self.assertEqual(len(response.data), 1)
  
      def test_unknown_filter(self):
          """
          GET requests with filters that aren't configured should return 200.
          """
          view = FilterFieldsRootView.as_view()
  
          search_integer = 10
          request = factory.get('/', {'integer': '%s' % search_integer})
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
  
      def test_html_rendering(self):
          """
          Make sure response renders w/ backend
          """
          view = FilterFieldsRootView.as_view()
          request = factory.get('/')
          request.META['HTTP_ACCEPT'] = 'text/html'
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
  
      def test_raise_validation_error(self):
          """
          Ensure validation errors return a proper error response instead of
          an internal server error.
          """
          view = FilterFieldsRootView.as_view()
          request = factory.get('/?decimal=foobar')
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
          self.assertEqual(response.data, {'decimal': ['Enter a number.']})
  
      def test_permissive(self):
          """
          Permissive handling should return a partially filtered result set.
          """
          FilterableItem.objects.create(decimal=Decimal('1.23'), date='2017-01-01')
          FilterableItem.objects.create(decimal=Decimal('1.23'), date='2016-01-01')
  
          class Backend(DjangoFilterBackend):
              raise_exception = False
  
          class View(FilterFieldsRootView):
              filter_backends = (Backend,)
  
          view = View.as_view()
          request = factory.get('/?decimal=foobar&date=2017-01-01')
          response = view(request).render()
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          self.assertEqual(response.data[0]['date'], '2017-01-01')
          self.assertEqual(len(response.data), 1)
  
  
  @override_settings(ROOT_URLCONF='tests.rest_framework.test_integration')
  class IntegrationTestDetailFiltering(CommonFilteringTestCase):
      """
      Integration tests for filtered detail views.
      """
  
      def _get_url(self, item):
          return reverse('detail-view', kwargs=dict(pk=item.pk))
  
      def test_get_filtered_detail_view(self):
          """
          GET requests to filtered RetrieveAPIView that have a filterset_class
          set should return filtered results.
          """
          item = self.objects.all()[0]
          data = self._serialize_object(item)
  
          # Basic test with no filter.
          response = self.client.get(self._get_url(item))
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          self.assertEqual(response.data, data)
  
          # Tests that the decimal filter set that should fail.
          search_decimal = Decimal('4.25')
          high_item = self.objects.filter(decimal__gt=search_decimal)[0]
          response = self.client.get(
              '{url}'.format(url=self._get_url(high_item)),
              {'decimal': '{param}'.format(param=search_decimal)})
          self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
  
          # Tests that the decimal filter set that should succeed.
          search_decimal = Decimal('4.25')
          low_item = self.objects.filter(decimal__lt=search_decimal)[0]
          low_item_data = self._serialize_object(low_item)
          response = self.client.get(
              '{url}'.format(url=self._get_url(low_item)),
              {'decimal': '{param}'.format(param=search_decimal)})
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          self.assertEqual(response.data, low_item_data)
  
          # Tests that multiple filters works.
          search_decimal = Decimal('5.25')
          search_date = datetime.date(2012, 10, 2)
          valid_item = self.objects.filter(decimal__lt=search_decimal, date__gt=search_date)[0]
          valid_item_data = self._serialize_object(valid_item)
          response = self.client.get(
              '{url}'.format(url=self._get_url(valid_item)), {
                  'decimal': '{decimal}'.format(decimal=search_decimal),
                  'date': '{date}'.format(date=search_date)
              })
          self.assertEqual(response.status_code, status.HTTP_200_OK)
          self.assertEqual(response.data, valid_item_data)
  
  
  class DjangoFilterOrderingSerializer(serializers.ModelSerializer):
      class Meta:
          model = DjangoFilterOrderingModel
          fields = '__all__'
  
  
  class DjangoFilterOrderingTests(TestCase):
      def setUp(self):
          data = [{
              'date': datetime.date(2012, 10, 8),
              'text': 'abc'
          }, {
              'date': datetime.date(2013, 10, 8),
              'text': 'bcd'
          }, {
              'date': datetime.date(2014, 10, 8),
              'text': 'cde'
          }]
  
          for d in data:
              DjangoFilterOrderingModel.objects.create(**d)
  
      def test_default_ordering(self):
          class DjangoFilterOrderingView(generics.ListAPIView):
              serializer_class = DjangoFilterOrderingSerializer
              queryset = DjangoFilterOrderingModel.objects.all()
              filter_backends = (DjangoFilterBackend,)
              filterset_fields = ['text']
              ordering = ('-date',)
  
          view = DjangoFilterOrderingView.as_view()
          request = factory.get('/')
          response = view(request)
  
          self.assertEqual(
              response.data,
              [
                  {'id': 3, 'date': '2014-10-08', 'text': 'cde'},
                  {'id': 2, 'date': '2013-10-08', 'text': 'bcd'},
                  {'id': 1, 'date': '2012-10-08', 'text': 'abc'}
              ]
          )
  