Skip to content

Commit 66189be

Browse files
committed
Filtering now available on nodes without defining fields
1 parent afd7aa8 commit 66189be

File tree

7 files changed

+76
-21
lines changed

7 files changed

+76
-21
lines changed

graphene/contrib/django/fields.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ def __init__(self, *args, **kwargs):
2020

2121

2222
class ConnectionOrListField(Field):
23+
connection_field_class = ConnectionField
2324

2425
def internal_type(self, schema):
2526
model_field = self.type
2627
field_object_type = model_field.get_object_type(schema)
2728
if not field_object_type:
2829
raise SkipField()
2930
if is_node(field_object_type):
30-
field = ConnectionField(field_object_type)
31+
field = self.connection_field_class(field_object_type)
3132
else:
3233
field = Field(List(field_object_type))
3334
field.contribute_to_class(self.object_type, self.attname)

graphene/contrib/django/filter/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
try:
2-
import django_filters # noqa
3-
except:
1+
from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
2+
3+
if not DJANGO_FILTER_INSTALLED:
44
raise Exception(
55
"Use of django filtering requires the django-filter package "
66
"be installed. You can do so using `pip install django-filter`"

graphene/contrib/django/options.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
from .utils import DJANGO_FILTER_INSTALLED
12
from ...core.classtypes.objecttype import ObjectTypeOptions
23
from ...relay.types import Node
34
from ...relay.utils import is_node
45

5-
VALID_ATTRS = ('model', 'only_fields', 'exclude_fields',
6-
'filter_fields', 'filter_order_by')
6+
VALID_ATTRS = ('model', 'only_fields', 'exclude_fields')
7+
8+
if DJANGO_FILTER_INSTALLED:
9+
VALID_ATTRS += ('filter_fields', 'filter_order_by')
710

811

912
class DjangoOptions(ObjectTypeOptions):

graphene/contrib/django/tests/filter/test_fields.py

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
import pytest
22

3-
pytestmark = []
3+
from graphene import ObjectType, Schema
4+
from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
5+
from graphene.relay import NodeField
46

5-
try:
6-
import django_filters
7-
except ImportError:
8-
pytestmark.append(pytest.mark.skipif(True, reason='django_filters not installed'))
9-
else:
10-
from graphene.contrib.django.filter import (GlobalIDFilter, DjangoFilterConnectionField,
11-
GlobalIDMultipleChoiceFilter)
12-
from graphene.contrib.django.tests.filter.filters import ArticleFilter, PetFilter
137

148
from graphene.contrib.django import DjangoNode
159
from graphene.contrib.django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
1610
from graphene.contrib.django.tests.models import Article, Pet, Reporter
1711

12+
pytestmark = []
13+
if DJANGO_FILTER_INSTALLED:
14+
import django_filters
15+
from graphene.contrib.django.filter import (GlobalIDFilter, DjangoFilterConnectionField,
16+
GlobalIDMultipleChoiceFilter)
17+
from graphene.contrib.django.tests.filter.filters import ArticleFilter, PetFilter
18+
else:
19+
pytestmark.append(pytest.mark.skipif(True, reason='django_filters not installed'))
20+
1821
pytestmark.append(pytest.mark.django_db)
1922

2023

@@ -124,6 +127,32 @@ class Meta:
124127
assert_orderable(field)
125128

126129

130+
def test_filter_filterset_information_on_meta_related():
131+
class ReporterFilterNode(DjangoNode):
132+
class Meta:
133+
model = Reporter
134+
filter_fields = ['first_name', 'articles']
135+
filter_order_by = True
136+
137+
class ArticleFilterNode(DjangoNode):
138+
class Meta:
139+
model = Article
140+
filter_fields = ['headline', 'reporter']
141+
filter_order_by = True
142+
143+
class Query(ObjectType):
144+
all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
145+
all_articles = DjangoFilterConnectionField(ArticleFilterNode)
146+
reporter = NodeField(ReporterFilterNode)
147+
article = NodeField(ArticleFilterNode)
148+
149+
schema = Schema(query=Query)
150+
schema.schema # Trigger the schema loading
151+
articles_field = schema.get_type('ReporterFilterNode')._meta.fields_map['articles']
152+
assert_arguments(articles_field, 'headline', 'reporter')
153+
assert_orderable(articles_field)
154+
155+
127156
def test_global_id_field_implicit():
128157
field = DjangoFilterConnectionField(ArticleNode, fields=['id'])
129158
filterset_class = field.resolver_fn.get_filterset_class()

graphene/contrib/django/tests/filter/test_resolvers.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import pytest
22
from django.core.exceptions import ImproperlyConfigured
33

4-
try:
5-
import django_filters # noqa
6-
except ImportError:
7-
pytestmark = pytest.mark.skipif(True, reason='django_filters not installed')
8-
else:
4+
from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
5+
6+
if DJANGO_FILTER_INSTALLED:
97
from graphene.contrib.django.filter.resolvers import FilterConnectionResolver
108
from graphene.contrib.django.tests.filter.filters import ReporterFilter, ArticleFilter
9+
else:
10+
pytestmark = pytest.mark.skipif(True, reason='django_filters not installed')
1111

1212
from graphene.contrib.django.tests.models import Reporter, Article
1313
from graphene.contrib.django.tests.test_resolvers import ReporterNode, ArticleNode

graphene/contrib/django/types.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
77
from ...relay.types import Connection, Node, NodeMeta
8+
from .utils import DJANGO_FILTER_INSTALLED
89
from .converter import convert_django_field
910
from .options import DjangoOptions
1011
from .utils import get_reverse_fields, maybe_queryset
@@ -49,6 +50,15 @@ def construct(cls, *args, **kwargs):
4950
return cls
5051

5152

53+
class DjangoFilterObjectTypeMeta():
54+
55+
def convert_django_field(cls, field):
56+
from graphene.contrib.django.filter import DjangoFilterConnectionField
57+
field = super(DjangoFilterObjectTypeMeta, cls).convert_django_field(field)
58+
field.connection_field_class = DjangoFilterConnectionField
59+
return field
60+
61+
5262
class InstanceObjectType(ObjectType):
5363

5464
class Meta:
@@ -92,7 +102,13 @@ def from_list(cls, iterable, *args, **kwargs):
92102
return super(DjangoConnection, cls).from_list(iterable, *args, **kwargs)
93103

94104

95-
class DjangoNodeMeta(DjangoObjectTypeMeta, NodeMeta):
105+
django_node_meta_bases = (DjangoObjectTypeMeta, NodeMeta)
106+
# Only include filter functionality if available
107+
if DJANGO_FILTER_INSTALLED:
108+
django_node_meta_bases = (DjangoFilterObjectTypeMeta,) + django_node_meta_bases
109+
110+
111+
class DjangoNodeMeta(*django_node_meta_bases):
96112
pass
97113

98114

graphene/contrib/django/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77

88
from graphene import Argument, String
99

10+
try:
11+
import django_filters # noqa
12+
DJANGO_FILTER_INSTALLED = True
13+
except ImportError:
14+
DJANGO_FILTER_INSTALLED = False
15+
1016

1117
def get_type_for_model(schema, model):
1218
schema = schema

0 commit comments

Comments
 (0)