Moving django-filter dependent code into graphene.contrib.django.filter

Graphene should now run fine without django-filter. Tests will
also run without django-filter. However, I'm leaving it as a
requirement in setup.py's `tests_require` setting as testing
without it is probably not to be encouraged.
This commit is contained in:
Adam Charnock 2015-12-03 20:01:37 +00:00
parent 3709f9450b
commit fb45a83925
13 changed files with 190 additions and 170 deletions

View File

@ -5,10 +5,8 @@ from graphene.contrib.django.types import (
)
from graphene.contrib.django.fields import (
DjangoConnectionField,
DjangoModelField,
DjangoFilterConnectionField
DjangoModelField
)
__all__ = ['DjangoObjectType', 'DjangoNode', 'DjangoConnection',
'DjangoConnectionField', 'DjangoModelField',
'DjangoFilterConnectionField']
'DjangoModelField']

View File

@ -1,7 +1,5 @@
import warnings
from graphene.contrib.django.utils import get_filtering_args_from_filterset
from .resolvers import FilterConnectionResolver
from .utils import get_type_for_model
from ...core.exceptions import SkipField
from ...core.fields import Field
@ -62,23 +60,3 @@ class DjangoModelField(FieldType):
return get_type_for_model(schema, self.model)
class DjangoFilterConnectionField(DjangoConnectionField):
def __init__(self, type, on=None, fields=None, order_by=None,
extra_filter_meta=None, filterset_class=None, resolver=None,
*args, **kwargs):
if not resolver:
resolver = FilterConnectionResolver(
node=type,
on=on,
filterset_class=filterset_class,
fields=fields,
order_by=order_by,
extra_filter_meta=extra_filter_meta,
)
filtering_args = get_filtering_args_from_filterset(resolver.get_filterset_class(), type)
kwargs.setdefault('args', {})
kwargs['args'].update(**filtering_args)
super(DjangoFilterConnectionField, self).__init__(type, resolver, *args, **kwargs)

View File

@ -0,0 +1,3 @@
from .fields import DjangoFilterConnectionField
from .filterset import GrapheneFilterSet, GlobalIDFilter
from .resolvers import FilterConnectionResolver

View File

@ -0,0 +1,25 @@
from graphene.contrib.django import DjangoConnectionField
from graphene.contrib.django.filter.resolvers import FilterConnectionResolver
from graphene.contrib.django.utils import get_filtering_args_from_filterset
class DjangoFilterConnectionField(DjangoConnectionField):
def __init__(self, type, on=None, fields=None, order_by=None,
extra_filter_meta=None, filterset_class=None, resolver=None,
*args, **kwargs):
if not resolver:
resolver = FilterConnectionResolver(
node=type,
on=on,
filterset_class=filterset_class,
fields=fields,
order_by=order_by,
extra_filter_meta=extra_filter_meta,
)
filtering_args = get_filtering_args_from_filterset(resolver.get_filterset_class(), type)
kwargs.setdefault('args', {})
kwargs['args'].update(**filtering_args)
super(DjangoFilterConnectionField, self).__init__(type, resolver, *args, **kwargs)

View File

@ -0,0 +1,63 @@
from django.core.exceptions import ImproperlyConfigured
from graphene.contrib.django.filter.filterset import setup_filterset, custom_filterset_factory
from graphene.contrib.django.resolvers import BaseQuerySetConnectionResolver
class FilterConnectionResolver(BaseQuerySetConnectionResolver):
# Querying using django-filter
def __init__(self, node, on=None, filterset_class=None,
fields=None, order_by=None, extra_filter_meta=None):
self.filterset_class = filterset_class
self.fields = fields
self.order_by = order_by
self.extra_filter_meta = extra_filter_meta or {}
self._filterset_class = None
super(FilterConnectionResolver, self).__init__(node, on)
def make_query(self):
filterset_class = self.get_filterset_class()
filterset = self.get_filterset(filterset_class)
return filterset.qs
def get_filterset_class(self):
"""Get the class to be used as the FilterSet"""
if self._filterset_class:
return self._filterset_class
if self.filterset_class:
# If were given a FilterSet class, then set it up and
# return it
self._filterset_class = setup_filterset(self.filterset_class)
elif self.model:
# If no filter class was specified then create one given the
# other information provided
meta = dict(
model=self.model,
fields=self.fields,
order_by=self.order_by,
)
meta.update(self.extra_filter_meta)
self._filterset_class = custom_filterset_factory(**meta)
else:
msg = "Neither 'filterset_class' or 'model' available in '%s'. " \
"Either pass in 'filterset_class' or 'model' when " \
"initialising, or extend this class and override " \
"get_filterset() or get_filterset_class()"
raise ImproperlyConfigured(msg % self.__class__.__name__)
return self._filterset_class
def get_filterset(self, filterset_class):
"""Get an instance of the FilterSet"""
kwargs = self.get_filterset_kwargs(filterset_class)
return filterset_class(**kwargs)
def get_filterset_kwargs(self, filterset_class):
"""Get the kwargs to use when initialising the FilterSet class"""
kwargs = {
'data': self.args or None,
'queryset': self.get_manager()
}
return kwargs

View File

@ -1,8 +1,3 @@
from django.core.exceptions import ImproperlyConfigured
from graphene.contrib.django.filterset import setup_filterset, custom_filterset_factory
class BaseQuerySetConnectionResolver(object):
def __init__(self, node, on=None):
@ -48,60 +43,3 @@ class SimpleQuerySetConnectionResolver(BaseQuerySetConnectionResolver):
return self.args.get('order', None)
class FilterConnectionResolver(BaseQuerySetConnectionResolver):
# Querying using django-filter
def __init__(self, node, on=None, filterset_class=None,
fields=None, order_by=None, extra_filter_meta=None):
self.filterset_class = filterset_class
self.fields = fields
self.order_by = order_by
self.extra_filter_meta = extra_filter_meta or {}
self._filterset_class = None
super(FilterConnectionResolver, self).__init__(node, on)
def make_query(self):
filterset_class = self.get_filterset_class()
filterset = self.get_filterset(filterset_class)
return filterset.qs
def get_filterset_class(self):
"""Get the class to be used as the FilterSet"""
if self._filterset_class:
return self._filterset_class
if self.filterset_class:
# If were given a FilterSet class, then set it up and
# return it
self._filterset_class = setup_filterset(self.filterset_class)
elif self.model:
# If no filter class was specified then create one given the
# other information provided
meta = dict(
model=self.model,
fields=self.fields,
order_by=self.order_by,
)
meta.update(self.extra_filter_meta)
self._filterset_class = custom_filterset_factory(**meta)
else:
msg = "Neither 'filterset_class' or 'model' available in '%s'. " \
"Either pass in 'filterset_class' or 'model' when " \
"initialising, or extend this class and override " \
"get_filterset() or get_filterset_class()"
raise ImproperlyConfigured(msg % self.__class__.__name__)
return self._filterset_class
def get_filterset(self, filterset_class):
"""Get an instance of the FilterSet"""
kwargs = self.get_filterset_kwargs(filterset_class)
return filterset_class(**kwargs)
def get_filterset_kwargs(self, filterset_class):
"""Get the kwargs to use when initialising the FilterSet class"""
kwargs = {
'data': self.args or None,
'queryset': self.get_manager()
}
return kwargs

View File

@ -1,7 +1,7 @@
import django_filters
from graphene.contrib.django.tests.models import Reporter
from .models import Article, Pet
from graphene.contrib.django.tests.models import Article, Pet
class ArticleFilter(django_filters.FilterSet):

View File

@ -1,9 +1,15 @@
import django_filters
import pytest
from graphene.contrib.django import DjangoFilterConnectionField, DjangoNode
from graphene.contrib.django.filterset import GlobalIDFilter
try:
import django_filters
except ImportError:
pytestmark = pytest.mark.skipif(True, reason='django_filters not installed')
else:
from graphene.contrib.django.filter import GlobalIDFilter, DjangoFilterConnectionField
from graphene.contrib.django.tests.filter.filters import ArticleFilter, PetFilter
from graphene.contrib.django import DjangoNode
from graphene.contrib.django.forms import GlobalIDFormField
from graphene.contrib.django.tests.filters import ArticleFilter, PetFilter
from graphene.contrib.django.tests.models import Article, Pet

View File

@ -0,0 +1,84 @@
import pytest
from django.core.exceptions import ImproperlyConfigured
try:
import django_filters
except ImportError:
pytestmark = pytest.mark.skipif(True, reason='django_filters not installed')
else:
from graphene.contrib.django.filter.resolvers import FilterConnectionResolver
from graphene.contrib.django.tests.filter.filters import ReporterFilter, ArticleFilter
from graphene.contrib.django.tests.models import Reporter, Article
from graphene.contrib.django.tests.test_resolvers import ReporterNode, ArticleNode
def test_filter_get_filterset_class_explicit():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolver(inst=reporter, args={}, info=None)
assert issubclass(resolver.get_filterset_class(), ReporterFilter), \
'ReporterFilter not returned'
def test_filter_get_filterset_class_implicit():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode)
resolver(inst=reporter, args={}, info=None)
assert resolver.get_filterset_class().__name__ == 'ReporterFilterSet'
def test_filter_get_filterset_class_error():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode)
resolver.model = None
with pytest.raises(ImproperlyConfigured) as excinfo:
resolver(inst=reporter, args={}, info=None)
assert "Neither 'filterset_class' or 'model' available" in str(excinfo.value)
def test_filter_filter():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolved = resolver(inst=reporter, args={
'first_name': 'Elmo'
}, info=None)
assert '"first_name" = Elmo' in str(resolved.query)
assert 'ORDER BY' not in str(resolved.query)
def test_filter_filter_contains():
article = Article(id=1, headline='Cookie Monster eats fruit')
resolver = FilterConnectionResolver(ArticleNode,
filterset_class=ArticleFilter)
resolved = resolver(inst=article, args={
'headline__icontains': 'Elmo'
}, info=None)
assert '"headline" LIKE %Elmo%' in str(resolved.query)
def test_filter_order():
article = Article(id=1, headline='Cookie Monster eats fruit')
resolver = FilterConnectionResolver(ArticleNode,
filterset_class=ArticleFilter)
resolved = resolver(inst=article, args={
# TODO: This should be 'order', not 'o'
'o': 'headline'
}, info=None)
assert 'WHERE' not in str(resolved.query)
assert 'ORDER BY' in str(resolved.query)
assert '"headline" ASC' in str(resolved.query)
def test_filter_order_not_available():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolved = resolver(inst=reporter, args={
# TODO: This should be 'order', not 'o'
'o': 'last_name'
}, info=None)
assert 'WHERE' not in str(resolved.query)
assert 'ORDER BY' not in str(resolved.query)

View File

@ -1,11 +1,8 @@
from django.core.exceptions import ImproperlyConfigured
from py.test import raises
from django.db.models import Manager
from django.db.models.query import QuerySet
from graphene.contrib.django import DjangoNode
from graphene.contrib.django.resolvers import SimpleQuerySetConnectionResolver, FilterConnectionResolver
from graphene.contrib.django.tests.filters import ReporterFilter, ArticleFilter
from graphene.contrib.django.resolvers import SimpleQuerySetConnectionResolver
from graphene.contrib.django.tests.models import Reporter, Article
@ -59,74 +56,3 @@ def test_simple_order():
assert 'WHERE' not in str(resolved.query)
assert 'ORDER BY' in str(resolved.query)
assert '"last_name" ASC' in str(resolved.query)
def test_filter_get_filterset_class_explicit():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolver(inst=reporter, args={}, info=None)
assert issubclass(resolver.get_filterset_class(), ReporterFilter), \
'ReporterFilter not returned'
def test_filter_get_filterset_class_implicit():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode)
resolver(inst=reporter, args={}, info=None)
assert resolver.get_filterset_class().__name__ == 'ReporterFilterSet'
def test_filter_get_filterset_class_error():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode)
resolver.model = None
with raises(ImproperlyConfigured) as excinfo:
resolver(inst=reporter, args={}, info=None)
assert "Neither 'filterset_class' or 'model' available" in str(excinfo.value)
def test_filter_filter():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolved = resolver(inst=reporter, args={
'first_name': 'Elmo'
}, info=None)
assert '"first_name" = Elmo' in str(resolved.query)
assert 'ORDER BY' not in str(resolved.query)
def test_filter_filter_contains():
article = Article(id=1, headline='Cookie Monster eats fruit')
resolver = FilterConnectionResolver(ArticleNode,
filterset_class=ArticleFilter)
resolved = resolver(inst=article, args={
'headline__icontains': 'Elmo'
}, info=None)
assert '"headline" LIKE %Elmo%' in str(resolved.query)
def test_filter_order():
article = Article(id=1, headline='Cookie Monster eats fruit')
resolver = FilterConnectionResolver(ArticleNode,
filterset_class=ArticleFilter)
resolved = resolver(inst=article, args={
# TODO: This should be 'order', not 'o'
'o': 'headline'
}, info=None)
assert 'WHERE' not in str(resolved.query)
assert 'ORDER BY' in str(resolved.query)
assert '"headline" ASC' in str(resolved.query)
def test_filter_order_not_available():
reporter = Reporter(id=1, first_name='Cookie Monster')
resolver = FilterConnectionResolver(ReporterNode,
filterset_class=ReporterFilter)
resolved = resolver(inst=reporter, args={
# TODO: This should be 'order', not 'o'
'o': 'last_name'
}, info=None)
assert 'WHERE' not in str(resolved.query)
assert 'ORDER BY' not in str(resolved.query)

View File

@ -57,10 +57,9 @@ setup(
'six>=1.10.0',
'graphql-core==0.4.9',
'graphql-relay==0.3.3',
'django_filter>=0.10.0',
],
tests_require=[
'django-filter>=0.11.0',
'django-filter>=0.10.0',
'pytest>=2.7.2',
'pytest-django',
'mock',