mirror of
https://github.com/graphql-python/graphene.git
synced 2024-11-10 19:56:45 +03:00
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:
parent
3709f9450b
commit
fb45a83925
|
@ -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']
|
||||
|
|
|
@ -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)
|
||||
|
|
3
graphene/contrib/django/filter/__init__.py
Normal file
3
graphene/contrib/django/filter/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from .fields import DjangoFilterConnectionField
|
||||
from .filterset import GrapheneFilterSet, GlobalIDFilter
|
||||
from .resolvers import FilterConnectionResolver
|
25
graphene/contrib/django/filter/fields.py
Normal file
25
graphene/contrib/django/filter/fields.py
Normal 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)
|
63
graphene/contrib/django/filter/resolvers.py
Normal file
63
graphene/contrib/django/filter/resolvers.py
Normal 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
|
|
@ -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
|
||||
|
|
0
graphene/contrib/django/tests/filter/__init__.py
Normal file
0
graphene/contrib/django/tests/filter/__init__.py
Normal 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):
|
|
@ -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
|
||||
|
||||
|
84
graphene/contrib/django/tests/filter/test_resolvers.py
Normal file
84
graphene/contrib/django/tests/filter/test_resolvers.py
Normal 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)
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user