diff --git a/.travis.yml b/.travis.yml index 10fcb823e..3e21ae593 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,9 +22,8 @@ before_install: install: - | if [ "$TEST_TYPE" = build ]; then + pip install pytest==3.0.2 pytest-cov pytest-benchmark coveralls six pytest-django==2.9.1 mock django-filter pip install django==$DJANGO_VERSION - pip install pytest==3.0.2 pytest-cov pytest-benchmark coveralls six pytest-django==2.9.1 mock - pip install django==$DJANGO_FILTER_VERSION pip install -e . python setup.py develop elif [ "$TEST_TYPE" = lint ]; then @@ -46,18 +45,18 @@ after_success: fi env: matrix: - - TEST_TYPE=build DJANGO_VERSION=1.10 DJANGO_FILTER_VERSION=1.0.0 + - TEST_TYPE=build DJANGO_VERSION=1.10 matrix: fast_finish: true include: - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.6 DJANGO_FILTER_VERSION=0.15.3 + env: TEST_TYPE=build DJANGO_VERSION=1.6 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.7 DJANGO_FILTER_VERSION=0.15.3 + env: TEST_TYPE=build DJANGO_VERSION=1.7 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.8 DJANGO_FILTER_VERSION=0.15.3 + env: TEST_TYPE=build DJANGO_VERSION=1.8 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.9 DJANGO_FILTER_VERSION=1.0.0 + env: TEST_TYPE=build DJANGO_VERSION=1.9 - python: '2.7' env: TEST_TYPE=lint deploy: diff --git a/docs/filtering.rst b/docs/filtering.rst index f2ea0e1f0..f6ad8828f 100644 --- a/docs/filtering.rst +++ b/docs/filtering.rst @@ -91,50 +91,13 @@ Which you could query as follows: } } -Orderable fields ----------------- - -Ordering can also be specified using ``filter_order_by``. Like -``filter_fields``, this value is also passed directly to -``django-filter`` as the ``order_by`` field. For full details see the -`order\_by -documentation `__. - -For example: - -.. code:: python - - class AnimalNode(DjangoObjectType): - class Meta: - model = Animal - filter_fields = ['name', 'genus', 'is_domesticated'] - # Either a tuple/list of fields upon which ordering is allowed, or - # True to allow filtering on all fields specified in filter_fields - filter_order_by = True - interfaces = (relay.Node, ) - -You can then control the ordering via the ``orderBy`` argument: - -.. code:: - - query { - allAnimals(orderBy: "name") { - edges { - node { - id, - name - } - } - } - } - Custom Filtersets ----------------- By default Graphene provides easy access to the most commonly used features of ``django-filter``. This is done by transparently creating a ``django_filters.FilterSet`` class for you and passing in the values for -``filter_fields`` and ``filter_order_by``. +``filter_fields``. However, you may find this to be insufficient. In these cases you can create your own ``Filterset`` as follows: diff --git a/docs/tutorial.rst b/docs/tutorial.rst index e1537a335..cd201b9c0 100644 --- a/docs/tutorial.rst +++ b/docs/tutorial.rst @@ -98,7 +98,6 @@ Create ``cookbook/ingredients/schema.py`` and type the following: class Meta: model = Category filter_fields = ['name', 'ingredients'] - filter_order_by = ['name'] interfaces = (relay.Node, ) @@ -112,7 +111,6 @@ Create ``cookbook/ingredients/schema.py`` and type the following: 'category': ['exact'], 'category__name': ['exact'], } - filter_order_by = ['name', 'category__name'] interfaces = (relay.Node, ) diff --git a/graphene_django/filter/__init__.py b/graphene_django/filter/__init__.py index 71616b6d7..24fae60f1 100644 --- a/graphene_django/filter/__init__.py +++ b/graphene_django/filter/__init__.py @@ -8,7 +8,7 @@ ) else: from .fields import DjangoFilterConnectionField - from .filterset import GrapheneFilterSet, GlobalIDFilter, GlobalIDMultipleChoiceFilter + from .filterset import GlobalIDFilter, GlobalIDMultipleChoiceFilter - __all__ = ['DjangoFilterConnectionField', 'GrapheneFilterSet', + __all__ = ['DjangoFilterConnectionField', 'GlobalIDFilter', 'GlobalIDMultipleChoiceFilter'] diff --git a/graphene_django/filter/fields.py b/graphene_django/filter/fields.py index bd4b44da9..defcfc166 100644 --- a/graphene_django/filter/fields.py +++ b/graphene_django/filter/fields.py @@ -6,15 +6,12 @@ class DjangoFilterConnectionField(DjangoConnectionField): - def __init__(self, type, fields=None, order_by=None, - extra_filter_meta=None, filterset_class=None, - *args, **kwargs): + def __init__(self, type, fields=None, extra_filter_meta=None, + filterset_class=None, *args, **kwargs): - self.order_by = order_by or type._meta.filter_order_by self.fields = fields or type._meta.filter_fields meta = dict(model=type._meta.model, - fields=self.fields, - order_by=self.order_by) + fields=self.fields) if extra_filter_meta: meta.update(extra_filter_meta) self.filterset_class = get_filterset_class(filterset_class, **meta) @@ -27,12 +24,8 @@ def __init__(self, type, fields=None, order_by=None, def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args, root, args, context, info): filter_kwargs = {k: v for k, v in args.items() if k in filtering_args} - order = args.get('order_by', None) qs = default_manager.get_queryset() - if order: - qs = qs.order_by(order) - qs = filterset_class(data=filter_kwargs, queryset=qs) - + qs = filterset_class(data=filter_kwargs, queryset=qs).qs return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info) def get_resolver(self, parent_resolver): diff --git a/graphene_django/filter/filterset.py b/graphene_django/filter/filterset.py index 6df50ce8a..c716b05cd 100644 --- a/graphene_django/filter/filterset.py +++ b/graphene_django/filter/filterset.py @@ -1,11 +1,9 @@ import itertools -import six -from django.conf import settings from django.db import models from django.utils.text import capfirst from django_filters import Filter, MultipleChoiceFilter -from django_filters.filterset import FilterSet, FilterSetMetaclass +from django_filters.filterset import BaseFilterSet, FilterSet from django_filters.filterset import FILTER_FOR_DBFIELD_DEFAULTS from graphql_relay.node.node import from_global_id @@ -29,9 +27,6 @@ def filter(self, qs, value): return super(GlobalIDMultipleChoiceFilter, self).filter(qs, gids) -ORDER_BY_FIELD = getattr(settings, 'GRAPHENE_ORDER_BY_FIELD', 'order_by') - - GRAPHENE_FILTER_SET_OVERRIDES = { models.AutoField: { 'filter_class': GlobalIDFilter, @@ -48,25 +43,7 @@ def filter(self, qs, value): } -# Only useful for Django-filter 0.14-, not necessary in latest version 0.15+ -class GrapheneFilterSetMetaclass(FilterSetMetaclass): - - def __new__(cls, name, bases, attrs): - new_class = super(GrapheneFilterSetMetaclass, cls).__new__(cls, name, bases, attrs) - # Customise the filter_overrides for Graphene - if hasattr(new_class, '_meta') and hasattr(new_class._meta, 'filter_overrides'): - filter_overrides = new_class._meta.filter_overrides - else: - filter_overrides = new_class.filter_overrides - - for k, v in GRAPHENE_FILTER_SET_OVERRIDES.items(): - filter_overrides.setdefault(k, v) - - return new_class - - -class GrapheneFilterSetMixin(object): - order_by_field = ORDER_BY_FIELD +class GrapheneFilterSetMixin(BaseFilterSet): FILTER_DEFAULTS = dict(itertools.chain( FILTER_FOR_DBFIELD_DEFAULTS.items(), GRAPHENE_FILTER_SET_OVERRIDES.items() @@ -93,26 +70,17 @@ def filter_for_reverse_field(cls, f, name): return GlobalIDFilter(**default) -class GrapheneFilterSet(six.with_metaclass(GrapheneFilterSetMetaclass, GrapheneFilterSetMixin, FilterSet)): - """ Base class for FilterSets used by Graphene - - You shouldn't usually need to use this class. The - DjangoFilterConnectionField will wrap FilterSets with this class as - necessary - """ - - def setup_filterset(filterset_class): """ Wrap a provided filterset in Graphene-specific functionality """ return type( 'Graphene{}'.format(filterset_class.__name__), - (six.with_metaclass(GrapheneFilterSetMetaclass, GrapheneFilterSetMixin, filterset_class),), + (filterset_class, GrapheneFilterSetMixin), {}, ) -def custom_filterset_factory(model, filterset_base_class=GrapheneFilterSet, +def custom_filterset_factory(model, filterset_base_class=FilterSet, **meta): """ Create a filterset for the given model using the provided meta data """ @@ -122,7 +90,7 @@ def custom_filterset_factory(model, filterset_base_class=GrapheneFilterSet, meta_class = type(str('Meta'), (object,), meta) filterset = type( str('%sFilterSet' % model._meta.object_name), - (filterset_base_class,), + (filterset_base_class, GrapheneFilterSetMixin), { 'Meta': meta_class } diff --git a/graphene_django/filter/tests/filters.py b/graphene_django/filter/tests/filters.py index bb2f65722..4a3fbaa34 100644 --- a/graphene_django/filter/tests/filters.py +++ b/graphene_django/filter/tests/filters.py @@ -1,4 +1,5 @@ import django_filters +from django_filters import OrderingFilter from graphene_django.tests.models import Article, Pet, Reporter @@ -12,7 +13,8 @@ class Meta: 'pub_date': ['gt', 'lt', 'exact'], 'reporter': ['exact'], } - order_by = False + + order_by = OrderingFilter(fields=('pub_date',)) class ReporterFilter(django_filters.FilterSet): @@ -20,7 +22,8 @@ class ReporterFilter(django_filters.FilterSet): class Meta: model = Reporter fields = ['first_name', 'last_name', 'email', 'pets'] - order_by = True + + order_by = OrderingFilter(fields=('pub_date',)) class PetFilter(django_filters.FilterSet): @@ -28,4 +31,3 @@ class PetFilter(django_filters.FilterSet): class Meta: model = Pet fields = ['name'] - order_by = False diff --git a/graphene_django/filter/tests/test_fields.py b/graphene_django/filter/tests/test_fields.py index ab3d6779f..63c9e373c 100644 --- a/graphene_django/filter/tests/test_fields.py +++ b/graphene_django/filter/tests/test_fields.py @@ -11,6 +11,7 @@ from graphene_django.utils import DJANGO_FILTER_INSTALLED pytestmark = [] + if DJANGO_FILTER_INSTALLED: import django_filters from graphene_django.filter import (GlobalIDFilter, DjangoFilterConnectionField, @@ -22,27 +23,29 @@ pytestmark.append(pytest.mark.django_db) -class ArticleNode(DjangoObjectType): +if DJANGO_FILTER_INSTALLED: + class ArticleNode(DjangoObjectType): - class Meta: - model = Article - interfaces = (Node, ) + class Meta: + model = Article + interfaces = (Node, ) + filter_fields = ('headline', ) -class ReporterNode(DjangoObjectType): + class ReporterNode(DjangoObjectType): - class Meta: - model = Reporter - interfaces = (Node, ) + class Meta: + model = Reporter + interfaces = (Node, ) -class PetNode(DjangoObjectType): + class PetNode(DjangoObjectType): - class Meta: - model = Pet - interfaces = (Node, ) + class Meta: + model = Pet + interfaces = (Node, ) -# schema = Schema() + # schema = Schema() def get_args(field): @@ -110,8 +113,8 @@ def test_filter_explicit_filterset_orderable(): def test_filter_shortcut_filterset_orderable_true(): - field = DjangoFilterConnectionField(ReporterNode, order_by=True) - assert_orderable(field) + field = DjangoFilterConnectionField(ReporterNode) + assert_not_orderable(field) # def test_filter_shortcut_filterset_orderable_headline(): @@ -126,9 +129,9 @@ def test_filter_explicit_filterset_not_orderable(): def test_filter_shortcut_filterset_extra_meta(): field = DjangoFilterConnectionField(ArticleNode, extra_filter_meta={ - 'order_by': True + 'exclude': ('headline', ) }) - assert_orderable(field) + assert 'headline' not in field.filterset_class.get_fields() def test_filter_filterset_information_on_meta(): @@ -138,11 +141,10 @@ class Meta: model = Reporter interfaces = (Node, ) filter_fields = ['first_name', 'articles'] - filter_order_by = True field = DjangoFilterConnectionField(ReporterFilterNode) assert_arguments(field, 'first_name', 'articles') - assert_orderable(field) + assert_not_orderable(field) def test_filter_filterset_information_on_meta_related(): @@ -152,7 +154,6 @@ class Meta: model = Reporter interfaces = (Node, ) filter_fields = ['first_name', 'articles'] - filter_order_by = True class ArticleFilterNode(DjangoObjectType): @@ -160,7 +161,6 @@ class Meta: model = Article interfaces = (Node, ) filter_fields = ['headline', 'reporter'] - filter_order_by = True class Query(ObjectType): all_reporters = DjangoFilterConnectionField(ReporterFilterNode) @@ -171,7 +171,7 @@ class Query(ObjectType): schema = Schema(query=Query) articles_field = ReporterFilterNode._meta.fields['articles'].get_type() assert_arguments(articles_field, 'headline', 'reporter') - assert_orderable(articles_field) + assert_not_orderable(articles_field) def test_filter_filterset_related_results(): @@ -181,7 +181,6 @@ class Meta: model = Reporter interfaces = (Node, ) filter_fields = ['first_name', 'articles'] - filter_order_by = True class ArticleFilterNode(DjangoObjectType): @@ -189,7 +188,6 @@ class Meta: interfaces = (Node, ) model = Article filter_fields = ['headline', 'reporter'] - filter_order_by = True class Query(ObjectType): all_reporters = DjangoFilterConnectionField(ReporterFilterNode) diff --git a/graphene_django/filter/utils.py b/graphene_django/filter/utils.py index 20e271acd..6b938ce40 100644 --- a/graphene_django/filter/utils.py +++ b/graphene_django/filter/utils.py @@ -1,7 +1,5 @@ import six -from graphene import String - from .filterset import custom_filterset_factory, setup_filterset @@ -18,10 +16,6 @@ def get_filtering_args_from_filterset(filterset_class, type): field_type.description = filter_field.label args[name] = field_type - # Also add the 'order_by' field - if getattr(filterset_class._meta, 'order_by', None): - args[filterset_class.order_by_field] = String() - return args diff --git a/graphene_django/types.py b/graphene_django/types.py index 1973a8516..8174f05b9 100644 --- a/graphene_django/types.py +++ b/graphene_django/types.py @@ -65,7 +65,6 @@ def __new__(cls, name, bases, attrs): # we allow more attributes in Meta defaults.update( filter_fields=(), - filter_order_by=(), ) options = Options( diff --git a/setup.py b/setup.py index 9c556343d..4acd9474c 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ 'pytest-runner', ], tests_require=[ - 'django-filter>=0.10.0', + 'django-filter>=1.0.0', 'pytest', 'pytest-django==2.9.1', 'mock',