Filtering now available on nodes without defining fields

This commit is contained in:
Adam Charnock 2015-12-05 11:15:17 +00:00
parent afd7aa8d72
commit 66189be4a9
7 changed files with 76 additions and 21 deletions

View File

@ -20,6 +20,7 @@ class DjangoConnectionField(ConnectionField):
class ConnectionOrListField(Field): class ConnectionOrListField(Field):
connection_field_class = ConnectionField
def internal_type(self, schema): def internal_type(self, schema):
model_field = self.type model_field = self.type
@ -27,7 +28,7 @@ class ConnectionOrListField(Field):
if not field_object_type: if not field_object_type:
raise SkipField() raise SkipField()
if is_node(field_object_type): if is_node(field_object_type):
field = ConnectionField(field_object_type) field = self.connection_field_class(field_object_type)
else: else:
field = Field(List(field_object_type)) field = Field(List(field_object_type))
field.contribute_to_class(self.object_type, self.attname) field.contribute_to_class(self.object_type, self.attname)

View File

@ -1,6 +1,6 @@
try: from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
import django_filters # noqa
except: if not DJANGO_FILTER_INSTALLED:
raise Exception( raise Exception(
"Use of django filtering requires the django-filter package " "Use of django filtering requires the django-filter package "
"be installed. You can do so using `pip install django-filter`" "be installed. You can do so using `pip install django-filter`"

View File

@ -1,9 +1,12 @@
from .utils import DJANGO_FILTER_INSTALLED
from ...core.classtypes.objecttype import ObjectTypeOptions from ...core.classtypes.objecttype import ObjectTypeOptions
from ...relay.types import Node from ...relay.types import Node
from ...relay.utils import is_node from ...relay.utils import is_node
VALID_ATTRS = ('model', 'only_fields', 'exclude_fields', VALID_ATTRS = ('model', 'only_fields', 'exclude_fields')
'filter_fields', 'filter_order_by')
if DJANGO_FILTER_INSTALLED:
VALID_ATTRS += ('filter_fields', 'filter_order_by')
class DjangoOptions(ObjectTypeOptions): class DjangoOptions(ObjectTypeOptions):

View File

@ -1,20 +1,23 @@
import pytest import pytest
pytestmark = [] from graphene import ObjectType, Schema
from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
from graphene.relay import NodeField
try:
import django_filters
except ImportError:
pytestmark.append(pytest.mark.skipif(True, reason='django_filters not installed'))
else:
from graphene.contrib.django.filter import (GlobalIDFilter, DjangoFilterConnectionField,
GlobalIDMultipleChoiceFilter)
from graphene.contrib.django.tests.filter.filters import ArticleFilter, PetFilter
from graphene.contrib.django import DjangoNode from graphene.contrib.django import DjangoNode
from graphene.contrib.django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField from graphene.contrib.django.forms import GlobalIDFormField, GlobalIDMultipleChoiceField
from graphene.contrib.django.tests.models import Article, Pet, Reporter from graphene.contrib.django.tests.models import Article, Pet, Reporter
pytestmark = []
if DJANGO_FILTER_INSTALLED:
import django_filters
from graphene.contrib.django.filter import (GlobalIDFilter, DjangoFilterConnectionField,
GlobalIDMultipleChoiceFilter)
from graphene.contrib.django.tests.filter.filters import ArticleFilter, PetFilter
else:
pytestmark.append(pytest.mark.skipif(True, reason='django_filters not installed'))
pytestmark.append(pytest.mark.django_db) pytestmark.append(pytest.mark.django_db)
@ -124,6 +127,32 @@ def test_filter_filterset_information_on_meta():
assert_orderable(field) assert_orderable(field)
def test_filter_filterset_information_on_meta_related():
class ReporterFilterNode(DjangoNode):
class Meta:
model = Reporter
filter_fields = ['first_name', 'articles']
filter_order_by = True
class ArticleFilterNode(DjangoNode):
class Meta:
model = Article
filter_fields = ['headline', 'reporter']
filter_order_by = True
class Query(ObjectType):
all_reporters = DjangoFilterConnectionField(ReporterFilterNode)
all_articles = DjangoFilterConnectionField(ArticleFilterNode)
reporter = NodeField(ReporterFilterNode)
article = NodeField(ArticleFilterNode)
schema = Schema(query=Query)
schema.schema # Trigger the schema loading
articles_field = schema.get_type('ReporterFilterNode')._meta.fields_map['articles']
assert_arguments(articles_field, 'headline', 'reporter')
assert_orderable(articles_field)
def test_global_id_field_implicit(): def test_global_id_field_implicit():
field = DjangoFilterConnectionField(ArticleNode, fields=['id']) field = DjangoFilterConnectionField(ArticleNode, fields=['id'])
filterset_class = field.resolver_fn.get_filterset_class() filterset_class = field.resolver_fn.get_filterset_class()

View File

@ -1,13 +1,13 @@
import pytest import pytest
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
try: from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
import django_filters # noqa
except ImportError: if DJANGO_FILTER_INSTALLED:
pytestmark = pytest.mark.skipif(True, reason='django_filters not installed')
else:
from graphene.contrib.django.filter.resolvers import FilterConnectionResolver from graphene.contrib.django.filter.resolvers import FilterConnectionResolver
from graphene.contrib.django.tests.filter.filters import ReporterFilter, ArticleFilter from graphene.contrib.django.tests.filter.filters import ReporterFilter, ArticleFilter
else:
pytestmark = pytest.mark.skipif(True, reason='django_filters not installed')
from graphene.contrib.django.tests.models import Reporter, Article from graphene.contrib.django.tests.models import Reporter, Article
from graphene.contrib.django.tests.test_resolvers import ReporterNode, ArticleNode from graphene.contrib.django.tests.test_resolvers import ReporterNode, ArticleNode

View File

@ -5,6 +5,7 @@ from django.db import models
from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta from ...core.classtypes.objecttype import ObjectType, ObjectTypeMeta
from ...relay.types import Connection, Node, NodeMeta from ...relay.types import Connection, Node, NodeMeta
from .utils import DJANGO_FILTER_INSTALLED
from .converter import convert_django_field from .converter import convert_django_field
from .options import DjangoOptions from .options import DjangoOptions
from .utils import get_reverse_fields, maybe_queryset from .utils import get_reverse_fields, maybe_queryset
@ -49,6 +50,15 @@ class DjangoObjectTypeMeta(ObjectTypeMeta):
return cls return cls
class DjangoFilterObjectTypeMeta():
def convert_django_field(cls, field):
from graphene.contrib.django.filter import DjangoFilterConnectionField
field = super(DjangoFilterObjectTypeMeta, cls).convert_django_field(field)
field.connection_field_class = DjangoFilterConnectionField
return field
class InstanceObjectType(ObjectType): class InstanceObjectType(ObjectType):
class Meta: class Meta:
@ -92,7 +102,13 @@ class DjangoConnection(Connection):
return super(DjangoConnection, cls).from_list(iterable, *args, **kwargs) return super(DjangoConnection, cls).from_list(iterable, *args, **kwargs)
class DjangoNodeMeta(DjangoObjectTypeMeta, NodeMeta): django_node_meta_bases = (DjangoObjectTypeMeta, NodeMeta)
# Only include filter functionality if available
if DJANGO_FILTER_INSTALLED:
django_node_meta_bases = (DjangoFilterObjectTypeMeta,) + django_node_meta_bases
class DjangoNodeMeta(*django_node_meta_bases):
pass pass

View File

@ -7,6 +7,12 @@ from graphene.utils import LazyList
from graphene import Argument, String from graphene import Argument, String
try:
import django_filters # noqa
DJANGO_FILTER_INSTALLED = True
except ImportError:
DJANGO_FILTER_INSTALLED = False
def get_type_for_model(schema, model): def get_type_for_model(schema, model):
schema = schema schema = schema