From 982c5ff1a67ffe3c028d24a67f9152e61edf61f5 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Wed, 21 Sep 2016 17:43:07 -0400 Subject: [PATCH 1/3] Port get_schema_fields from DRF --- django_filters/rest_framework/backends.py | 14 +++++++++- tests/rest_framework/test_backends.py | 31 +++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/django_filters/rest_framework/backends.py b/django_filters/rest_framework/backends.py index 4027ac2e0..eefc5fe9d 100644 --- a/django_filters/rest_framework/backends.py +++ b/django_filters/rest_framework/backends.py @@ -2,7 +2,7 @@ from __future__ import absolute_import from django.template import Template, TemplateDoesNotExist, loader -from rest_framework.compat import template_render +from rest_framework.compat import coreapi, template_render from rest_framework.filters import BaseFilterBackend from .. import compat @@ -89,3 +89,15 @@ def to_html(self, request, queryset, view): return template_render(template, context={ 'filter': filter_instance }) + + def get_schema_fields(self, view): + # This is not compatible with widgets where the query param differs from the + # filter's attribute name. Notably, this includes `MultiWidget`, where query + # params will be of the format `_0`, `_1`, etc... + assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + filter_class = self.get_filter_class(view, view.get_queryset()) + + return [] if not filter_class else [ + coreapi.Field(name=field_name, required=False, location='query') + for field_name in filter_class().filters.keys() + ] diff --git a/tests/rest_framework/test_backends.py b/tests/rest_framework/test_backends.py index f6b383c69..496eecd1c 100644 --- a/tests/rest_framework/test_backends.py +++ b/tests/rest_framework/test_backends.py @@ -2,6 +2,7 @@ import datetime from decimal import Decimal +from unittest import skipIf from django.conf.urls import url from django.test import TestCase @@ -15,6 +16,7 @@ from django.core.urlresolvers import reverse from rest_framework import generics, serializers, status +from rest_framework.compat import coreapi from rest_framework.test import APIRequestFactory from django_filters import filters @@ -121,6 +123,35 @@ def get_queryset(self): ] +@skipIf(coreapi is None, 'coreapi must be installed') +class GetSchemaFieldsTests(TestCase): + def test_fields_with_filter_fields_list(self): + backend = DjangoFilterBackend() + fields = backend.get_schema_fields(FilterFieldsRootView()) + fields = [f.name for f in fields] + + self.assertEqual(fields, ['decimal', 'date']) + + def test_fields_with_filter_fields_dict(self): + class DictFilterFieldsRootView(FilterFieldsRootView): + filter_fields = { + 'decimal': ['exact', 'lt', 'gt'], + } + + backend = DjangoFilterBackend() + fields = backend.get_schema_fields(DictFilterFieldsRootView()) + fields = [f.name for f in fields] + + self.assertEqual(fields, ['decimal', 'decimal__lt', 'decimal__gt']) + + def test_fields_with_filter_class(self): + backend = DjangoFilterBackend() + fields = backend.get_schema_fields(FilterClassRootView()) + fields = [f.name for f in fields] + + self.assertEqual(fields, ['text', 'decimal', 'date']) + + class CommonFilteringTestCase(TestCase): def _serialize_object(self, obj): return {'id': obj.id, 'text': obj.text, 'decimal': str(obj.decimal), 'date': obj.date.isoformat()} From 0b1ec0507ba43fb391d0225efc8c8836e0702a4d Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Sun, 16 Oct 2016 19:06:46 -0400 Subject: [PATCH 2/3] Add coreapi to ci, fix test reqs --- requirements/test-ci.txt | 2 ++ requirements/test.txt | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/requirements/test-ci.txt b/requirements/test-ci.txt index 85ceb08a8..2c79c1d17 100644 --- a/requirements/test-ci.txt +++ b/requirements/test-ci.txt @@ -1,3 +1,5 @@ +coreapi + coverage mock pytz diff --git a/requirements/test.txt b/requirements/test.txt index 5b885fa47..b811569ac 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,3 +1,3 @@ --r travis-ci.txt +-r test-ci.txt django djangorestframework From bc359782fdc298044d9da0af0081b53c70ee7a2c Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Sun, 16 Oct 2016 19:13:12 -0400 Subject: [PATCH 3/3] Add coreapi/DRF compatibility --- django_filters/compat.py | 9 +++++++++ django_filters/rest_framework/backends.py | 6 +++--- tests/rest_framework/test_backends.py | 5 ++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/django_filters/compat.py b/django_filters/compat.py index e7fa88b2d..cbac451ba 100644 --- a/django_filters/compat.py +++ b/django_filters/compat.py @@ -1,4 +1,6 @@ +from __future__ import absolute_import + import django from django.conf import settings @@ -12,6 +14,13 @@ is_crispy = 'crispy_forms' in settings.INSTALLED_APPS and crispy_forms +# coreapi only compatible with DRF 3.4+ +try: + from rest_framework.compat import coreapi +except ImportError: + coreapi = None + + def remote_field(field): """ https://docs.djangoproject.com/en/1.9/releases/1.9/#field-rel-changes diff --git a/django_filters/rest_framework/backends.py b/django_filters/rest_framework/backends.py index eefc5fe9d..37c7dfa57 100644 --- a/django_filters/rest_framework/backends.py +++ b/django_filters/rest_framework/backends.py @@ -2,7 +2,7 @@ from __future__ import absolute_import from django.template import Template, TemplateDoesNotExist, loader -from rest_framework.compat import coreapi, template_render +from rest_framework.compat import template_render from rest_framework.filters import BaseFilterBackend from .. import compat @@ -94,10 +94,10 @@ def get_schema_fields(self, view): # This is not compatible with widgets where the query param differs from the # filter's attribute name. Notably, this includes `MultiWidget`, where query # params will be of the format `_0`, `_1`, etc... - assert coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' + assert compat.coreapi is not None, 'coreapi must be installed to use `get_schema_fields()`' filter_class = self.get_filter_class(view, view.get_queryset()) return [] if not filter_class else [ - coreapi.Field(name=field_name, required=False, location='query') + compat.coreapi.Field(name=field_name, required=False, location='query') for field_name in filter_class().filters.keys() ] diff --git a/tests/rest_framework/test_backends.py b/tests/rest_framework/test_backends.py index 496eecd1c..763d09383 100644 --- a/tests/rest_framework/test_backends.py +++ b/tests/rest_framework/test_backends.py @@ -16,10 +16,9 @@ from django.core.urlresolvers import reverse from rest_framework import generics, serializers, status -from rest_framework.compat import coreapi from rest_framework.test import APIRequestFactory -from django_filters import filters +from django_filters import compat, filters from django_filters.rest_framework import DjangoFilterBackend, FilterSet from django_filters.rest_framework import backends @@ -123,7 +122,7 @@ def get_queryset(self): ] -@skipIf(coreapi is None, 'coreapi must be installed') +@skipIf(compat.coreapi is None, 'coreapi must be installed') class GetSchemaFieldsTests(TestCase): def test_fields_with_filter_fields_list(self): backend = DjangoFilterBackend()