Skip to content

Commit 37f8464

Browse files
authored
Merge pull request #65 from graphql-python/features/django-filter-1-0
Updated Django Filter integration to 1.0
2 parents 48993dd + 9d35b76 commit 37f8464

File tree

11 files changed

+46
-132
lines changed

11 files changed

+46
-132
lines changed

.travis.yml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ before_install:
2222
install:
2323
- |
2424
if [ "$TEST_TYPE" = build ]; then
25+
pip install pytest==3.0.2 pytest-cov pytest-benchmark coveralls six pytest-django==2.9.1 mock django-filter
2526
pip install django==$DJANGO_VERSION
26-
pip install pytest==3.0.2 pytest-cov pytest-benchmark coveralls six pytest-django==2.9.1 mock
27-
pip install django==$DJANGO_FILTER_VERSION
2827
pip install -e .
2928
python setup.py develop
3029
elif [ "$TEST_TYPE" = lint ]; then
@@ -46,18 +45,18 @@ after_success:
4645
fi
4746
env:
4847
matrix:
49-
- TEST_TYPE=build DJANGO_VERSION=1.10 DJANGO_FILTER_VERSION=1.0.0
48+
- TEST_TYPE=build DJANGO_VERSION=1.10
5049
matrix:
5150
fast_finish: true
5251
include:
5352
- python: '2.7'
54-
env: TEST_TYPE=build DJANGO_VERSION=1.6 DJANGO_FILTER_VERSION=0.15.3
53+
env: TEST_TYPE=build DJANGO_VERSION=1.6
5554
- python: '2.7'
56-
env: TEST_TYPE=build DJANGO_VERSION=1.7 DJANGO_FILTER_VERSION=0.15.3
55+
env: TEST_TYPE=build DJANGO_VERSION=1.7
5756
- python: '2.7'
58-
env: TEST_TYPE=build DJANGO_VERSION=1.8 DJANGO_FILTER_VERSION=0.15.3
57+
env: TEST_TYPE=build DJANGO_VERSION=1.8
5958
- python: '2.7'
60-
env: TEST_TYPE=build DJANGO_VERSION=1.9 DJANGO_FILTER_VERSION=1.0.0
59+
env: TEST_TYPE=build DJANGO_VERSION=1.9
6160
- python: '2.7'
6261
env: TEST_TYPE=lint
6362
deploy:

docs/filtering.rst

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -91,50 +91,13 @@ Which you could query as follows:
9191
}
9292
}
9393
94-
Orderable fields
95-
----------------
96-
97-
Ordering can also be specified using ``filter_order_by``. Like
98-
``filter_fields``, this value is also passed directly to
99-
``django-filter`` as the ``order_by`` field. For full details see the
100-
`order\_by
101-
documentation <https://django-filter.readthedocs.org/en/latest/usage.html#ordering-using-order-by>`__.
102-
103-
For example:
104-
105-
.. code:: python
106-
107-
class AnimalNode(DjangoObjectType):
108-
class Meta:
109-
model = Animal
110-
filter_fields = ['name', 'genus', 'is_domesticated']
111-
# Either a tuple/list of fields upon which ordering is allowed, or
112-
# True to allow filtering on all fields specified in filter_fields
113-
filter_order_by = True
114-
interfaces = (relay.Node, )
115-
116-
You can then control the ordering via the ``orderBy`` argument:
117-
118-
.. code::
119-
120-
query {
121-
allAnimals(orderBy: "name") {
122-
edges {
123-
node {
124-
id,
125-
name
126-
}
127-
}
128-
}
129-
}
130-
13194
Custom Filtersets
13295
-----------------
13396

13497
By default Graphene provides easy access to the most commonly used
13598
features of ``django-filter``. This is done by transparently creating a
13699
``django_filters.FilterSet`` class for you and passing in the values for
137-
``filter_fields`` and ``filter_order_by``.
100+
``filter_fields``.
138101

139102
However, you may find this to be insufficient. In these cases you can
140103
create your own ``Filterset`` as follows:

docs/tutorial.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ Create ``cookbook/ingredients/schema.py`` and type the following:
9898
class Meta:
9999
model = Category
100100
filter_fields = ['name', 'ingredients']
101-
filter_order_by = ['name']
102101
interfaces = (relay.Node, )
103102
104103
@@ -112,7 +111,6 @@ Create ``cookbook/ingredients/schema.py`` and type the following:
112111
'category': ['exact'],
113112
'category__name': ['exact'],
114113
}
115-
filter_order_by = ['name', 'category__name']
116114
interfaces = (relay.Node, )
117115
118116

graphene_django/filter/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
)
99
else:
1010
from .fields import DjangoFilterConnectionField
11-
from .filterset import GrapheneFilterSet, GlobalIDFilter, GlobalIDMultipleChoiceFilter
11+
from .filterset import GlobalIDFilter, GlobalIDMultipleChoiceFilter
1212

13-
__all__ = ['DjangoFilterConnectionField', 'GrapheneFilterSet',
13+
__all__ = ['DjangoFilterConnectionField',
1414
'GlobalIDFilter', 'GlobalIDMultipleChoiceFilter']

graphene_django/filter/fields.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,12 @@
66

77
class DjangoFilterConnectionField(DjangoConnectionField):
88

9-
def __init__(self, type, fields=None, order_by=None,
10-
extra_filter_meta=None, filterset_class=None,
11-
*args, **kwargs):
9+
def __init__(self, type, fields=None, extra_filter_meta=None,
10+
filterset_class=None, *args, **kwargs):
1211

13-
self.order_by = order_by or type._meta.filter_order_by
1412
self.fields = fields or type._meta.filter_fields
1513
meta = dict(model=type._meta.model,
16-
fields=self.fields,
17-
order_by=self.order_by)
14+
fields=self.fields)
1815
if extra_filter_meta:
1916
meta.update(extra_filter_meta)
2017
self.filterset_class = get_filterset_class(filterset_class, **meta)
@@ -27,12 +24,8 @@ def __init__(self, type, fields=None, order_by=None,
2724
def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args,
2825
root, args, context, info):
2926
filter_kwargs = {k: v for k, v in args.items() if k in filtering_args}
30-
order = args.get('order_by', None)
3127
qs = default_manager.get_queryset()
32-
if order:
33-
qs = qs.order_by(order)
34-
qs = filterset_class(data=filter_kwargs, queryset=qs)
35-
28+
qs = filterset_class(data=filter_kwargs, queryset=qs).qs
3629
return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info)
3730

3831
def get_resolver(self, parent_resolver):

graphene_django/filter/filterset.py

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import itertools
22

3-
import six
4-
from django.conf import settings
53
from django.db import models
64
from django.utils.text import capfirst
75
from django_filters import Filter, MultipleChoiceFilter
8-
from django_filters.filterset import FilterSet, FilterSetMetaclass
6+
from django_filters.filterset import BaseFilterSet, FilterSet
97
from django_filters.filterset import FILTER_FOR_DBFIELD_DEFAULTS
108

119
from graphql_relay.node.node import from_global_id
@@ -29,9 +27,6 @@ def filter(self, qs, value):
2927
return super(GlobalIDMultipleChoiceFilter, self).filter(qs, gids)
3028

3129

32-
ORDER_BY_FIELD = getattr(settings, 'GRAPHENE_ORDER_BY_FIELD', 'order_by')
33-
34-
3530
GRAPHENE_FILTER_SET_OVERRIDES = {
3631
models.AutoField: {
3732
'filter_class': GlobalIDFilter,
@@ -48,25 +43,7 @@ def filter(self, qs, value):
4843
}
4944

5045

51-
# Only useful for Django-filter 0.14-, not necessary in latest version 0.15+
52-
class GrapheneFilterSetMetaclass(FilterSetMetaclass):
53-
54-
def __new__(cls, name, bases, attrs):
55-
new_class = super(GrapheneFilterSetMetaclass, cls).__new__(cls, name, bases, attrs)
56-
# Customise the filter_overrides for Graphene
57-
if hasattr(new_class, '_meta') and hasattr(new_class._meta, 'filter_overrides'):
58-
filter_overrides = new_class._meta.filter_overrides
59-
else:
60-
filter_overrides = new_class.filter_overrides
61-
62-
for k, v in GRAPHENE_FILTER_SET_OVERRIDES.items():
63-
filter_overrides.setdefault(k, v)
64-
65-
return new_class
66-
67-
68-
class GrapheneFilterSetMixin(object):
69-
order_by_field = ORDER_BY_FIELD
46+
class GrapheneFilterSetMixin(BaseFilterSet):
7047
FILTER_DEFAULTS = dict(itertools.chain(
7148
FILTER_FOR_DBFIELD_DEFAULTS.items(),
7249
GRAPHENE_FILTER_SET_OVERRIDES.items()
@@ -93,26 +70,17 @@ def filter_for_reverse_field(cls, f, name):
9370
return GlobalIDFilter(**default)
9471

9572

96-
class GrapheneFilterSet(six.with_metaclass(GrapheneFilterSetMetaclass, GrapheneFilterSetMixin, FilterSet)):
97-
""" Base class for FilterSets used by Graphene
98-
99-
You shouldn't usually need to use this class. The
100-
DjangoFilterConnectionField will wrap FilterSets with this class as
101-
necessary
102-
"""
103-
104-
10573
def setup_filterset(filterset_class):
10674
""" Wrap a provided filterset in Graphene-specific functionality
10775
"""
10876
return type(
10977
'Graphene{}'.format(filterset_class.__name__),
110-
(six.with_metaclass(GrapheneFilterSetMetaclass, GrapheneFilterSetMixin, filterset_class),),
78+
(filterset_class, GrapheneFilterSetMixin),
11179
{},
11280
)
11381

11482

115-
def custom_filterset_factory(model, filterset_base_class=GrapheneFilterSet,
83+
def custom_filterset_factory(model, filterset_base_class=FilterSet,
11684
**meta):
11785
""" Create a filterset for the given model using the provided meta data
11886
"""
@@ -122,7 +90,7 @@ def custom_filterset_factory(model, filterset_base_class=GrapheneFilterSet,
12290
meta_class = type(str('Meta'), (object,), meta)
12391
filterset = type(
12492
str('%sFilterSet' % model._meta.object_name),
125-
(filterset_base_class,),
93+
(filterset_base_class, GrapheneFilterSetMixin),
12694
{
12795
'Meta': meta_class
12896
}

graphene_django/filter/tests/filters.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import django_filters
2+
from django_filters import OrderingFilter
23

34
from graphene_django.tests.models import Article, Pet, Reporter
45

@@ -12,20 +13,21 @@ class Meta:
1213
'pub_date': ['gt', 'lt', 'exact'],
1314
'reporter': ['exact'],
1415
}
15-
order_by = False
16+
17+
order_by = OrderingFilter(fields=('pub_date',))
1618

1719

1820
class ReporterFilter(django_filters.FilterSet):
1921

2022
class Meta:
2123
model = Reporter
2224
fields = ['first_name', 'last_name', 'email', 'pets']
23-
order_by = True
25+
26+
order_by = OrderingFilter(fields=('pub_date',))
2427

2528

2629
class PetFilter(django_filters.FilterSet):
2730

2831
class Meta:
2932
model = Pet
3033
fields = ['name']
31-
order_by = False

graphene_django/filter/tests/test_fields.py

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from graphene_django.utils import DJANGO_FILTER_INSTALLED
1212

1313
pytestmark = []
14+
1415
if DJANGO_FILTER_INSTALLED:
1516
import django_filters
1617
from graphene_django.filter import (GlobalIDFilter, DjangoFilterConnectionField,
@@ -22,27 +23,29 @@
2223
pytestmark.append(pytest.mark.django_db)
2324

2425

25-
class ArticleNode(DjangoObjectType):
26+
if DJANGO_FILTER_INSTALLED:
27+
class ArticleNode(DjangoObjectType):
2628

27-
class Meta:
28-
model = Article
29-
interfaces = (Node, )
29+
class Meta:
30+
model = Article
31+
interfaces = (Node, )
32+
filter_fields = ('headline', )
3033

3134

32-
class ReporterNode(DjangoObjectType):
35+
class ReporterNode(DjangoObjectType):
3336

34-
class Meta:
35-
model = Reporter
36-
interfaces = (Node, )
37+
class Meta:
38+
model = Reporter
39+
interfaces = (Node, )
3740

3841

39-
class PetNode(DjangoObjectType):
42+
class PetNode(DjangoObjectType):
4043

41-
class Meta:
42-
model = Pet
43-
interfaces = (Node, )
44+
class Meta:
45+
model = Pet
46+
interfaces = (Node, )
4447

45-
# schema = Schema()
48+
# schema = Schema()
4649

4750

4851
def get_args(field):
@@ -110,8 +113,8 @@ def test_filter_explicit_filterset_orderable():
110113

111114

112115
def test_filter_shortcut_filterset_orderable_true():
113-
field = DjangoFilterConnectionField(ReporterNode, order_by=True)
114-
assert_orderable(field)
116+
field = DjangoFilterConnectionField(ReporterNode)
117+
assert_not_orderable(field)
115118

116119

117120
# def test_filter_shortcut_filterset_orderable_headline():
@@ -126,9 +129,9 @@ def test_filter_explicit_filterset_not_orderable():
126129

127130
def test_filter_shortcut_filterset_extra_meta():
128131
field = DjangoFilterConnectionField(ArticleNode, extra_filter_meta={
129-
'order_by': True
132+
'exclude': ('headline', )
130133
})
131-
assert_orderable(field)
134+
assert 'headline' not in field.filterset_class.get_fields()
132135

133136

134137
def test_filter_filterset_information_on_meta():
@@ -138,11 +141,10 @@ class Meta:
138141
model = Reporter
139142
interfaces = (Node, )
140143
filter_fields = ['first_name', 'articles']
141-
filter_order_by = True
142144

143145
field = DjangoFilterConnectionField(ReporterFilterNode)
144146
assert_arguments(field, 'first_name', 'articles')
145-
assert_orderable(field)
147+
assert_not_orderable(field)
146148

147149

148150
def test_filter_filterset_information_on_meta_related():
@@ -152,15 +154,13 @@ class Meta:
152154
model = Reporter
153155
interfaces = (Node, )
154156
filter_fields = ['first_name', 'articles']
155-
filter_order_by = True
156157

157158
class ArticleFilterNode(DjangoObjectType):
158159

159160
class Meta:
160161
model = Article
161162
interfaces = (Node, )
162163
filter_fields = ['headline', 'reporter']
163-
filter_order_by = True
164164

165165
class Query(ObjectType):
166166
all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
@@ -171,7 +171,7 @@ class Query(ObjectType):
171171
schema = Schema(query=Query)
172172
articles_field = ReporterFilterNode._meta.fields['articles'].get_type()
173173
assert_arguments(articles_field, 'headline', 'reporter')
174-
assert_orderable(articles_field)
174+
assert_not_orderable(articles_field)
175175

176176

177177
def test_filter_filterset_related_results():
@@ -181,15 +181,13 @@ class Meta:
181181
model = Reporter
182182
interfaces = (Node, )
183183
filter_fields = ['first_name', 'articles']
184-
filter_order_by = True
185184

186185
class ArticleFilterNode(DjangoObjectType):
187186

188187
class Meta:
189188
interfaces = (Node, )
190189
model = Article
191190
filter_fields = ['headline', 'reporter']
192-
filter_order_by = True
193191

194192
class Query(ObjectType):
195193
all_reporters = DjangoFilterConnectionField(ReporterFilterNode)

0 commit comments

Comments
 (0)