From 7085437a12e6ae98e67d5183fc26057923b251b3 Mon Sep 17 00:00:00 2001 From: Niall Date: Sun, 5 Mar 2017 17:13:09 +0000 Subject: [PATCH 01/11] Fix filtering with a resolver and DjangoFilter filter. --- graphene_django/fields.py | 2 +- graphene_django/tests/test_query.py | 109 +++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/graphene_django/fields.py b/graphene_django/fields.py index 2519562..3e7c378 100644 --- a/graphene_django/fields.py +++ b/graphene_django/fields.py @@ -54,7 +54,7 @@ class DjangoConnectionField(ConnectionField): iterable = maybe_queryset(iterable) if isinstance(iterable, QuerySet): if iterable is not default_manager: - iterable &= maybe_queryset(default_manager) + iterable = maybe_queryset(default_manager) _len = iterable.count() else: _len = len(iterable) diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index 06b2bb3..486e5ff 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -5,12 +5,15 @@ from django.db import models from django.utils.functional import SimpleLazyObject from py.test import raises +from django_filters import FilterSet, NumberFilter + import graphene from graphene.relay import Node from ..utils import DJANGO_FILTER_INSTALLED from ..compat import MissingType, JSONField from ..fields import DjangoConnectionField +from ..filter.fields import DjangoFilterConnectionField from ..types import DjangoObjectType from .models import Article, Reporter @@ -42,7 +45,6 @@ def test_should_query_simplelazy_objects(): model = Reporter only_fields = ('id', ) - class Query(graphene.ObjectType): reporter = graphene.Field(ReporterType) @@ -360,7 +362,110 @@ def test_should_query_node_filtering(): }] } } - + + result = schema.execute(query) + assert not result.errors + assert result.data == expected + + +def test_should_query_filter_node_limit(): + class ReporterFilter(FilterSet): + limit = NumberFilter(method='filter_limit') + + def filter_limit(self, queryset, name, value): + return queryset[:value] + + class Meta: + model = Reporter + fields = ['first_name', ] + + class ReporterType(DjangoObjectType): + + class Meta: + model = Reporter + interfaces = (Node, ) + + class ArticleType(DjangoObjectType): + + class Meta: + model = Article + interfaces = (Node, ) + filter_fields = ('lang', ) + + class Query(graphene.ObjectType): + all_reporters = DjangoFilterConnectionField( + ReporterType, + filterset_class=ReporterFilter + ) + + def resolve_all_reporters(self, args, context, info): + return Reporter.objects.all() + + r = Reporter.objects.create( + first_name='John', + last_name='Doe', + email='johndoe@example.com', + a_choice=1 + ) + Reporter.objects.create( + first_name='Bob', + last_name='Doe', + email='bobdoe@example.com', + a_choice=1 + ) + + Article.objects.create( + headline='Article Node 1', + pub_date=datetime.date.today(), + reporter=r, + editor=r, + lang='es' + ) + Article.objects.create( + headline='Article Node 2', + pub_date=datetime.date.today(), + reporter=r, + editor=r, + lang='en' + ) + + schema = graphene.Schema(query=Query) + query = ''' + query NodeFilteringQuery { + allReporters(limit: 1) { + edges { + node { + id + articles(lang: "es") { + edges { + node { + id + } + } + } + } + } + } + } + ''' + + expected = { + 'allReporters': { + 'edges': [{ + 'node': { + 'id': 'UmVwb3J0ZXJUeXBlOjE=', + 'articles': { + 'edges': [{ + 'node': { + 'id': 'QXJ0aWNsZVR5cGU6MQ==' + } + }] + } + } + }] + } + } + result = schema.execute(query) assert not result.errors assert result.data == expected From 67804fdc091ff000807bec247365184e10ebcea3 Mon Sep 17 00:00:00 2001 From: Niall Date: Sun, 5 Mar 2017 19:17:00 +0000 Subject: [PATCH 02/11] Add broken test --- .gitignore | 1 + examples/starwars/tests/test_mutation.py | 1 + graphene_django/tests/models.py | 1 + graphene_django/tests/test_query.py | 92 ++++++++++++++++++++++++ 4 files changed, 95 insertions(+) diff --git a/.gitignore b/.gitignore index 0b25625..4e81c34 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] +.virtualenv # C extensions *.so diff --git a/examples/starwars/tests/test_mutation.py b/examples/starwars/tests/test_mutation.py index aa312ff..7c8019a 100644 --- a/examples/starwars/tests/test_mutation.py +++ b/examples/starwars/tests/test_mutation.py @@ -75,5 +75,6 @@ def test_mutations(): } } result = schema.execute(query) + print(result.data) assert not result.errors assert result.data == expected diff --git a/graphene_django/tests/models.py b/graphene_django/tests/models.py index 0c62f28..03ca59d 100644 --- a/graphene_django/tests/models.py +++ b/graphene_django/tests/models.py @@ -45,6 +45,7 @@ class Article(models.Model): ], default='es') importance = models.IntegerField('Importance', null=True, blank=True, choices=[(1, u'Very important'), (2, u'Not as important')]) + tag = models.CharField(max_length=100) def __str__(self): # __unicode__ on Python 2 return self.headline diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index 486e5ff..1f4809b 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -368,6 +368,98 @@ def test_should_query_node_filtering(): assert result.data == expected +@pytest.mark.skipif(not DJANGO_FILTER_INSTALLED, + reason="django-filter should be installed") +def test_should_query_node_multiple_filtering(): + class ReporterType(DjangoObjectType): + + class Meta: + model = Reporter + interfaces = (Node, ) + + class ArticleType(DjangoObjectType): + + class Meta: + model = Article + interfaces = (Node, ) + filter_fields = ('lang', 'tag') + + class Query(graphene.ObjectType): + all_reporters = DjangoConnectionField(ReporterType) + + r = Reporter.objects.create( + first_name='John', + last_name='Doe', + email='johndoe@example.com', + a_choice=1 + ) + Article.objects.create( + headline='Article Node 1', + pub_date=datetime.date.today(), + reporter=r, + editor=r, + lang='es', + tag='one' + ) + Article.objects.create( + headline='Article Node 2', + pub_date=datetime.date.today(), + reporter=r, + editor=r, + lang='en', + tag='two' + ) + Article.objects.create( + headline='Article Node 3', + pub_date=datetime.date.today(), + reporter=r, + editor=r, + lang='en', + tag='three' + ) + + schema = graphene.Schema(query=Query) + query = ''' + query NodeFilteringQuery { + allReporters { + edges { + node { + id + articles(lang: "es", tag: "two") { + edges { + node { + id + } + } + } + } + } + } + } + ''' + + expected = { + 'allReporters': { + 'edges': [{ + 'node': { + 'id': 'UmVwb3J0ZXJUeXBlOjE=', + 'articles': { + 'edges': [{ + 'node': { + 'id': 'QXJ0aWNsZVR5cGU6MQ==' + } + }] + } + } + }] + } + } + + result = schema.execute(query) + assert not result.errors + assert result.data == expected + + def test_should_query_filter_node_limit(): class ReporterFilter(FilterSet): limit = NumberFilter(method='filter_limit') From 69457cffdf2041b69fc46ca9fdfc6ae131fd51c9 Mon Sep 17 00:00:00 2001 From: Niall Date: Mon, 6 Mar 2017 18:13:40 +0000 Subject: [PATCH 03/11] Attempt fix. Breaks tests --- graphene_django/fields.py | 4 ++-- graphene_django/filter/fields.py | 27 ++++++++++++++++++++++----- graphene_django/tests/test_query.py | 13 +++++-------- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/graphene_django/fields.py b/graphene_django/fields.py index 3e7c378..9ba999c 100644 --- a/graphene_django/fields.py +++ b/graphene_django/fields.py @@ -53,8 +53,8 @@ class DjangoConnectionField(ConnectionField): iterable = default_manager iterable = maybe_queryset(iterable) if isinstance(iterable, QuerySet): - if iterable is not default_manager: - iterable = maybe_queryset(default_manager) + if default_manager is not None and iterable is not default_manager: + iterable &= maybe_queryset(default_manager) _len = iterable.count() else: _len = len(iterable) diff --git a/graphene_django/filter/fields.py b/graphene_django/filter/fields.py index 363e1d9..1b2c1c8 100644 --- a/graphene_django/filter/fields.py +++ b/graphene_django/filter/fields.py @@ -1,6 +1,8 @@ from collections import OrderedDict from functools import partial +from django.db.models.query import QuerySet + # from graphene.relay import is_node from graphene.types.argument import to_arguments from ..fields import DjangoConnectionField @@ -44,15 +46,30 @@ class DjangoFilterConnectionField(DjangoConnectionField): def filtering_args(self): return get_filtering_args_from_filterset(self.filterset_class, self.node_type) + # @staticmethod + # def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args, + # root, args, context, info): + # filter_kwargs = {k: v for k, v in args.items() if k in filtering_args} + # qs = filterset_class( + # data=filter_kwargs, + # queryset=default_manager.get_queryset() + # ).qs + # return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info) + @staticmethod def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args, root, args, context, info): filter_kwargs = {k: v for k, v in args.items() if k in filtering_args} - qs = filterset_class( - data=filter_kwargs, - queryset=default_manager.get_queryset() - ).qs - return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info) + + def new_resolver(root, args, context, info): + qs = resolver(root, args, context, info) + if qs is None or not isinstance(qs, QuerySet): + qs = default_manager.get_queryset() + qs = filterset_class(data=filter_kwargs, queryset=qs).qs + + return qs + + return DjangoConnectionField.connection_resolver(new_resolver, connection, None, root, args, context, info) def get_resolver(self, parent_resolver): return partial(self.connection_resolver, parent_resolver, self.type, self.get_manager(), diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index 1f4809b..6d2f8c8 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -382,7 +382,7 @@ def test_should_query_node_multiple_filtering(): class Meta: model = Article interfaces = (Node, ) - filter_fields = ('lang', 'tag') + filter_fields = ('lang', 'headline') class Query(graphene.ObjectType): all_reporters = DjangoConnectionField(ReporterType) @@ -398,24 +398,21 @@ def test_should_query_node_multiple_filtering(): pub_date=datetime.date.today(), reporter=r, editor=r, - lang='es', - tag='one' + lang='es' ) Article.objects.create( headline='Article Node 2', pub_date=datetime.date.today(), reporter=r, editor=r, - lang='en', - tag='two' + lang='en' ) Article.objects.create( headline='Article Node 3', pub_date=datetime.date.today(), reporter=r, editor=r, - lang='en', - tag='three' + lang='en' ) schema = graphene.Schema(query=Query) @@ -425,7 +422,7 @@ def test_should_query_node_multiple_filtering(): edges { node { id - articles(lang: "es", tag: "two") { + articles(lang: "es", headline: "Article Node 2") { edges { node { id From e2284fefb52954b4b012db1d565931f087aaf3d0 Mon Sep 17 00:00:00 2001 From: Niall Date: Mon, 6 Mar 2017 18:20:31 +0000 Subject: [PATCH 04/11] Clean up --- .gitignore | 1 - examples/starwars/tests/test_mutation.py | 1 - graphene_django/tests/models.py | 1 - 3 files changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index 4e81c34..0b25625 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] -.virtualenv # C extensions *.so diff --git a/examples/starwars/tests/test_mutation.py b/examples/starwars/tests/test_mutation.py index 7c8019a..aa312ff 100644 --- a/examples/starwars/tests/test_mutation.py +++ b/examples/starwars/tests/test_mutation.py @@ -75,6 +75,5 @@ def test_mutations(): } } result = schema.execute(query) - print(result.data) assert not result.errors assert result.data == expected diff --git a/graphene_django/tests/models.py b/graphene_django/tests/models.py index 03ca59d..0c62f28 100644 --- a/graphene_django/tests/models.py +++ b/graphene_django/tests/models.py @@ -45,7 +45,6 @@ class Article(models.Model): ], default='es') importance = models.IntegerField('Importance', null=True, blank=True, choices=[(1, u'Very important'), (2, u'Not as important')]) - tag = models.CharField(max_length=100) def __str__(self): # __unicode__ on Python 2 return self.headline From fda876fdc20756c712ffa579f8f37ffb8c158c3b Mon Sep 17 00:00:00 2001 From: Niall Date: Mon, 6 Mar 2017 19:41:04 +0000 Subject: [PATCH 05/11] Long-winded intersection using sets --- graphene_django/fields.py | 8 +++++--- graphene_django/filter/fields.py | 27 +++++---------------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/graphene_django/fields.py b/graphene_django/fields.py index 9ba999c..c7d9968 100644 --- a/graphene_django/fields.py +++ b/graphene_django/fields.py @@ -53,9 +53,11 @@ class DjangoConnectionField(ConnectionField): iterable = default_manager iterable = maybe_queryset(iterable) if isinstance(iterable, QuerySet): - if default_manager is not None and iterable is not default_manager: - iterable &= maybe_queryset(default_manager) - _len = iterable.count() + if iterable is not default_manager: + iterable = list(set(iterable).intersection(maybe_queryset(default_manager))) + _len = len(iterable) + else: + _len = iterable.count() else: _len = len(iterable) connection = connection_from_list_slice( diff --git a/graphene_django/filter/fields.py b/graphene_django/filter/fields.py index 1b2c1c8..363e1d9 100644 --- a/graphene_django/filter/fields.py +++ b/graphene_django/filter/fields.py @@ -1,8 +1,6 @@ from collections import OrderedDict from functools import partial -from django.db.models.query import QuerySet - # from graphene.relay import is_node from graphene.types.argument import to_arguments from ..fields import DjangoConnectionField @@ -46,30 +44,15 @@ class DjangoFilterConnectionField(DjangoConnectionField): def filtering_args(self): return get_filtering_args_from_filterset(self.filterset_class, self.node_type) - # @staticmethod - # def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args, - # root, args, context, info): - # filter_kwargs = {k: v for k, v in args.items() if k in filtering_args} - # qs = filterset_class( - # data=filter_kwargs, - # queryset=default_manager.get_queryset() - # ).qs - # return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info) - @staticmethod def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args, root, args, context, info): filter_kwargs = {k: v for k, v in args.items() if k in filtering_args} - - def new_resolver(root, args, context, info): - qs = resolver(root, args, context, info) - if qs is None or not isinstance(qs, QuerySet): - qs = default_manager.get_queryset() - qs = filterset_class(data=filter_kwargs, queryset=qs).qs - - return qs - - return DjangoConnectionField.connection_resolver(new_resolver, connection, None, root, args, context, info) + qs = filterset_class( + data=filter_kwargs, + queryset=default_manager.get_queryset() + ).qs + return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info) def get_resolver(self, parent_resolver): return partial(self.connection_resolver, parent_resolver, self.type, self.get_manager(), From 7210e308ec8476360e5aa1bee797e594d85f45dd Mon Sep 17 00:00:00 2001 From: Niall Date: Mon, 6 Mar 2017 20:00:01 +0000 Subject: [PATCH 06/11] Fix test --- graphene_django/tests/test_query.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index 6d2f8c8..4553259 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -405,7 +405,7 @@ def test_should_query_node_multiple_filtering(): pub_date=datetime.date.today(), reporter=r, editor=r, - lang='en' + lang='es' ) Article.objects.create( headline='Article Node 3', @@ -422,7 +422,7 @@ def test_should_query_node_multiple_filtering(): edges { node { id - articles(lang: "es", headline: "Article Node 2") { + articles(lang: "es", headline: "Article Node 1") { edges { node { id From 2117cb2b017334adc367098baa8d94e5cecdbdb8 Mon Sep 17 00:00:00 2001 From: Niall Date: Mon, 6 Mar 2017 20:19:39 +0000 Subject: [PATCH 07/11] Example for order_by being ignored --- graphene_django/tests/test_query.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index 4553259..75eb8c8 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -488,18 +488,18 @@ def test_should_query_filter_node_limit(): ) def resolve_all_reporters(self, args, context, info): - return Reporter.objects.all() + return Reporter.objects.order_by('a_choice') - r = Reporter.objects.create( - first_name='John', - last_name='Doe', - email='johndoe@example.com', - a_choice=1 - ) Reporter.objects.create( first_name='Bob', last_name='Doe', email='bobdoe@example.com', + a_choice=2 + ) + r = Reporter.objects.create( + first_name='John', + last_name='Doe', + email='johndoe@example.com', a_choice=1 ) From 5833cb83be1d0f96eff4ed37e6215822c480f9bf Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 15 Apr 2017 01:00:02 -0700 Subject: [PATCH 08/11] Fixed filterset limit issue --- graphene_django/fields.py | 15 ++++--- graphene_django/filter/fields.py | 27 +++++++++++- graphene_django/tests/test_query.py | 68 ++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 10 deletions(-) diff --git a/graphene_django/fields.py b/graphene_django/fields.py index c7d9968..367ad63 100644 --- a/graphene_django/fields.py +++ b/graphene_django/fields.py @@ -46,18 +46,21 @@ class DjangoConnectionField(ConnectionField): else: return self.model._default_manager - @staticmethod - def connection_resolver(resolver, connection, default_manager, root, args, context, info): + @classmethod + def merge_querysets(cls, default_queryset, queryset): + return default_queryset & queryset + + @classmethod + def connection_resolver(cls, resolver, connection, default_manager, root, args, context, info): iterable = resolver(root, args, context, info) if iterable is None: iterable = default_manager iterable = maybe_queryset(iterable) if isinstance(iterable, QuerySet): if iterable is not default_manager: - iterable = list(set(iterable).intersection(maybe_queryset(default_manager))) - _len = len(iterable) - else: - _len = iterable.count() + default_queryset = maybe_queryset(default_manager) + iterable = cls.merge_querysets(default_queryset, iterable) + _len = iterable.count() else: _len = len(iterable) connection = connection_from_list_slice( diff --git a/graphene_django/filter/fields.py b/graphene_django/filter/fields.py index 363e1d9..061b2c6 100644 --- a/graphene_django/filter/fields.py +++ b/graphene_django/filter/fields.py @@ -45,14 +45,37 @@ class DjangoFilterConnectionField(DjangoConnectionField): return get_filtering_args_from_filterset(self.filterset_class, self.node_type) @staticmethod - def connection_resolver(resolver, connection, default_manager, filterset_class, filtering_args, + def merge_querysets(default_queryset, queryset): + # There could be the case where the default queryset (returned from the filterclass) + # and the resolver queryset have some limits on it. + # We only would be able to apply one of those, but not both + # at the same time. + + # See related PR: https://github.com/graphql-python/graphene-django/pull/126 + + assert not (default_queryset.query.low_mark and queryset.query.low_mark), ( + 'Received two sliced querysets (low mark) in the connection, please slice only in one.' + ) + assert not (default_queryset.query.high_mark and queryset.query.high_mark), ( + 'Received two sliced querysets (high mark) in the connection, please slice only in one.' + ) + low = default_queryset.query.low_mark or queryset.query.low_mark + high = default_queryset.query.high_mark or queryset.query.high_mark + default_queryset.query.clear_limits() + queryset = default_queryset & queryset + queryset.query.set_limits(low, high) + return queryset + + @classmethod + def connection_resolver(cls, resolver, connection, default_manager, filterset_class, filtering_args, root, args, context, info): filter_kwargs = {k: v for k, v in args.items() if k in filtering_args} qs = filterset_class( data=filter_kwargs, queryset=default_manager.get_queryset() ).qs - return DjangoConnectionField.connection_resolver(resolver, connection, qs, root, args, context, info) + return super(DjangoFilterConnectionField, cls).connection_resolver( + resolver, connection, qs, root, args, context, info) def get_resolver(self, parent_resolver): return partial(self.connection_resolver, parent_resolver, self.type, self.get_manager(), diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index 75eb8c8..ae765e2 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -525,10 +525,12 @@ def test_should_query_filter_node_limit(): edges { node { id + firstName articles(lang: "es") { edges { node { id + lang } } } @@ -542,11 +544,13 @@ def test_should_query_filter_node_limit(): 'allReporters': { 'edges': [{ 'node': { - 'id': 'UmVwb3J0ZXJUeXBlOjE=', + 'id': 'UmVwb3J0ZXJUeXBlOjI=', + 'firstName': 'John', 'articles': { 'edges': [{ 'node': { - 'id': 'QXJ0aWNsZVR5cGU6MQ==' + 'id': 'QXJ0aWNsZVR5cGU6MQ==', + 'lang': 'ES' } }] } @@ -558,3 +562,63 @@ def test_should_query_filter_node_limit(): result = schema.execute(query) assert not result.errors assert result.data == expected + + +def test_should_query_filter_node_double_limit_raises(): + class ReporterFilter(FilterSet): + limit = NumberFilter(method='filter_limit') + + def filter_limit(self, queryset, name, value): + return queryset[:value] + + class Meta: + model = Reporter + fields = ['first_name', ] + + class ReporterType(DjangoObjectType): + + class Meta: + model = Reporter + interfaces = (Node, ) + + class Query(graphene.ObjectType): + all_reporters = DjangoFilterConnectionField( + ReporterType, + filterset_class=ReporterFilter + ) + + def resolve_all_reporters(self, args, context, info): + return Reporter.objects.order_by('a_choice')[:2] + + Reporter.objects.create( + first_name='Bob', + last_name='Doe', + email='bobdoe@example.com', + a_choice=2 + ) + r = Reporter.objects.create( + first_name='John', + last_name='Doe', + email='johndoe@example.com', + a_choice=1 + ) + + schema = graphene.Schema(query=Query) + query = ''' + query NodeFilteringQuery { + allReporters(limit: 1) { + edges { + node { + id + firstName + } + } + } + } + ''' + + result = schema.execute(query) + assert len(result.errors) == 1 + assert str(result.errors[0]) == ( + 'Received two sliced querysets (high mark) in the connection, please slice only in one.' + ) From b30b40e35361ff272cd9892efc4f62c9d77281c0 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 15 Apr 2017 01:11:36 -0700 Subject: [PATCH 09/11] Improved travis tests. Added Django==1.11 tests --- .travis.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index eb4799b..e18db52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,7 @@ install: pip install -e .[test] pip install psycopg2 # Required for Django postgres fields testing pip install django==$DJANGO_VERSION + pip install django-filter==$DJANGO_FILTER_VERSION python setup.py develop elif [ "$TEST_TYPE" = lint ]; then pip install flake8 @@ -45,18 +46,20 @@ after_success: fi env: matrix: - - TEST_TYPE=build DJANGO_VERSION=1.10 + - TEST_TYPE=build DJANGO_VERSION=1.11 DJANGO_FILTER_VERSION=1.0.2 matrix: fast_finish: true include: - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.6 + env: TEST_TYPE=build DJANGO_VERSION=1.6 DJANGO_FILTER_VERSION=1.0.0 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.7 + env: TEST_TYPE=build DJANGO_VERSION=1.7 DJANGO_FILTER_VERSION=1.0.0 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.8 + env: TEST_TYPE=build DJANGO_VERSION=1.8 DJANGO_FILTER_VERSION=1.0.2 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.9 + env: TEST_TYPE=build DJANGO_VERSION=1.9 DJANGO_FILTER_VERSION=1.0.2 + - python: '2.7' + env: TEST_TYPE=build DJANGO_VERSION=1.10 DJANGO_FILTER_VERSION=1.0.2 - python: '2.7' env: TEST_TYPE=lint deploy: From cc03da05988d3d5ecfc1c7b2c12303a8ac81b745 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 15 Apr 2017 01:16:11 -0700 Subject: [PATCH 10/11] Moved tests to filter field tests --- .travis.yml | 12 +- graphene_django/filter/tests/test_fields.py | 171 +++++++++++++++++++- graphene_django/tests/test_query.py | 170 ------------------- 3 files changed, 176 insertions(+), 177 deletions(-) diff --git a/.travis.yml b/.travis.yml index e18db52..3c9b44b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,20 +46,20 @@ after_success: fi env: matrix: - - TEST_TYPE=build DJANGO_VERSION=1.11 DJANGO_FILTER_VERSION=1.0.2 + - TEST_TYPE=build DJANGO_VERSION=1.11 matrix: fast_finish: true include: - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.6 DJANGO_FILTER_VERSION=1.0.0 + env: TEST_TYPE=build DJANGO_VERSION=1.6 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.7 DJANGO_FILTER_VERSION=1.0.0 + env: TEST_TYPE=build DJANGO_VERSION=1.7 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.8 DJANGO_FILTER_VERSION=1.0.2 + env: TEST_TYPE=build DJANGO_VERSION=1.8 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.9 DJANGO_FILTER_VERSION=1.0.2 + env: TEST_TYPE=build DJANGO_VERSION=1.9 - python: '2.7' - env: TEST_TYPE=build DJANGO_VERSION=1.10 DJANGO_FILTER_VERSION=1.0.2 + env: TEST_TYPE=build DJANGO_VERSION=1.10 - python: '2.7' env: TEST_TYPE=lint deploy: diff --git a/graphene_django/filter/tests/test_fields.py b/graphene_django/filter/tests/test_fields.py index c95e2d7..1b24ff2 100644 --- a/graphene_django/filter/tests/test_fields.py +++ b/graphene_django/filter/tests/test_fields.py @@ -14,11 +14,13 @@ pytestmark = [] if DJANGO_FILTER_INSTALLED: import django_filters + from django_filters import FilterSet, NumberFilter + from graphene_django.filter import (GlobalIDFilter, DjangoFilterConnectionField, GlobalIDMultipleChoiceFilter) from graphene_django.filter.tests.filters import ArticleFilter, PetFilter, ReporterFilter else: - pytestmark.append(pytest.mark.skipif(True, reason='django_filters not installed')) + pytestmark.append(pytest.mark.skipif(True, reason='django_filters not installed or not compatible')) pytestmark.append(pytest.mark.django_db) @@ -365,3 +367,170 @@ def test_recursive_filter_connection(): all_reporters = DjangoFilterConnectionField(ReporterFilterNode) assert ReporterFilterNode._meta.fields['child_reporters'].node_type == ReporterFilterNode + + +def test_should_query_filter_node_limit(): + class ReporterFilter(FilterSet): + limit = NumberFilter(method='filter_limit') + + def filter_limit(self, queryset, name, value): + return queryset[:value] + + class Meta: + model = Reporter + fields = ['first_name', ] + + class ReporterType(DjangoObjectType): + + class Meta: + model = Reporter + interfaces = (Node, ) + + class ArticleType(DjangoObjectType): + + class Meta: + model = Article + interfaces = (Node, ) + filter_fields = ('lang', ) + + class Query(ObjectType): + all_reporters = DjangoFilterConnectionField( + ReporterType, + filterset_class=ReporterFilter + ) + + def resolve_all_reporters(self, args, context, info): + return Reporter.objects.order_by('a_choice') + + Reporter.objects.create( + first_name='Bob', + last_name='Doe', + email='bobdoe@example.com', + a_choice=2 + ) + r = Reporter.objects.create( + first_name='John', + last_name='Doe', + email='johndoe@example.com', + a_choice=1 + ) + + Article.objects.create( + headline='Article Node 1', + pub_date=datetime.now(), + reporter=r, + editor=r, + lang='es' + ) + Article.objects.create( + headline='Article Node 2', + pub_date=datetime.now(), + reporter=r, + editor=r, + lang='en' + ) + + schema = Schema(query=Query) + query = ''' + query NodeFilteringQuery { + allReporters(limit: 1) { + edges { + node { + id + firstName + articles(lang: "es") { + edges { + node { + id + lang + } + } + } + } + } + } + } + ''' + + expected = { + 'allReporters': { + 'edges': [{ + 'node': { + 'id': 'UmVwb3J0ZXJUeXBlOjI=', + 'firstName': 'John', + 'articles': { + 'edges': [{ + 'node': { + 'id': 'QXJ0aWNsZVR5cGU6MQ==', + 'lang': 'ES' + } + }] + } + } + }] + } + } + + result = schema.execute(query) + assert not result.errors + assert result.data == expected + + +def test_should_query_filter_node_double_limit_raises(): + class ReporterFilter(FilterSet): + limit = NumberFilter(method='filter_limit') + + def filter_limit(self, queryset, name, value): + return queryset[:value] + + class Meta: + model = Reporter + fields = ['first_name', ] + + class ReporterType(DjangoObjectType): + + class Meta: + model = Reporter + interfaces = (Node, ) + + class Query(ObjectType): + all_reporters = DjangoFilterConnectionField( + ReporterType, + filterset_class=ReporterFilter + ) + + def resolve_all_reporters(self, args, context, info): + return Reporter.objects.order_by('a_choice')[:2] + + Reporter.objects.create( + first_name='Bob', + last_name='Doe', + email='bobdoe@example.com', + a_choice=2 + ) + r = Reporter.objects.create( + first_name='John', + last_name='Doe', + email='johndoe@example.com', + a_choice=1 + ) + + schema = Schema(query=Query) + query = ''' + query NodeFilteringQuery { + allReporters(limit: 1) { + edges { + node { + id + firstName + } + } + } + } + ''' + + result = schema.execute(query) + assert len(result.errors) == 1 + assert str(result.errors[0]) == ( + 'Received two sliced querysets (high mark) in the connection, please slice only in one.' + ) diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index ae765e2..3594000 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -5,15 +5,12 @@ from django.db import models from django.utils.functional import SimpleLazyObject from py.test import raises -from django_filters import FilterSet, NumberFilter - import graphene from graphene.relay import Node from ..utils import DJANGO_FILTER_INSTALLED from ..compat import MissingType, JSONField from ..fields import DjangoConnectionField -from ..filter.fields import DjangoFilterConnectionField from ..types import DjangoObjectType from .models import Article, Reporter @@ -455,170 +452,3 @@ def test_should_query_node_multiple_filtering(): result = schema.execute(query) assert not result.errors assert result.data == expected - - -def test_should_query_filter_node_limit(): - class ReporterFilter(FilterSet): - limit = NumberFilter(method='filter_limit') - - def filter_limit(self, queryset, name, value): - return queryset[:value] - - class Meta: - model = Reporter - fields = ['first_name', ] - - class ReporterType(DjangoObjectType): - - class Meta: - model = Reporter - interfaces = (Node, ) - - class ArticleType(DjangoObjectType): - - class Meta: - model = Article - interfaces = (Node, ) - filter_fields = ('lang', ) - - class Query(graphene.ObjectType): - all_reporters = DjangoFilterConnectionField( - ReporterType, - filterset_class=ReporterFilter - ) - - def resolve_all_reporters(self, args, context, info): - return Reporter.objects.order_by('a_choice') - - Reporter.objects.create( - first_name='Bob', - last_name='Doe', - email='bobdoe@example.com', - a_choice=2 - ) - r = Reporter.objects.create( - first_name='John', - last_name='Doe', - email='johndoe@example.com', - a_choice=1 - ) - - Article.objects.create( - headline='Article Node 1', - pub_date=datetime.date.today(), - reporter=r, - editor=r, - lang='es' - ) - Article.objects.create( - headline='Article Node 2', - pub_date=datetime.date.today(), - reporter=r, - editor=r, - lang='en' - ) - - schema = graphene.Schema(query=Query) - query = ''' - query NodeFilteringQuery { - allReporters(limit: 1) { - edges { - node { - id - firstName - articles(lang: "es") { - edges { - node { - id - lang - } - } - } - } - } - } - } - ''' - - expected = { - 'allReporters': { - 'edges': [{ - 'node': { - 'id': 'UmVwb3J0ZXJUeXBlOjI=', - 'firstName': 'John', - 'articles': { - 'edges': [{ - 'node': { - 'id': 'QXJ0aWNsZVR5cGU6MQ==', - 'lang': 'ES' - } - }] - } - } - }] - } - } - - result = schema.execute(query) - assert not result.errors - assert result.data == expected - - -def test_should_query_filter_node_double_limit_raises(): - class ReporterFilter(FilterSet): - limit = NumberFilter(method='filter_limit') - - def filter_limit(self, queryset, name, value): - return queryset[:value] - - class Meta: - model = Reporter - fields = ['first_name', ] - - class ReporterType(DjangoObjectType): - - class Meta: - model = Reporter - interfaces = (Node, ) - - class Query(graphene.ObjectType): - all_reporters = DjangoFilterConnectionField( - ReporterType, - filterset_class=ReporterFilter - ) - - def resolve_all_reporters(self, args, context, info): - return Reporter.objects.order_by('a_choice')[:2] - - Reporter.objects.create( - first_name='Bob', - last_name='Doe', - email='bobdoe@example.com', - a_choice=2 - ) - r = Reporter.objects.create( - first_name='John', - last_name='Doe', - email='johndoe@example.com', - a_choice=1 - ) - - schema = graphene.Schema(query=Query) - query = ''' - query NodeFilteringQuery { - allReporters(limit: 1) { - edges { - node { - id - firstName - } - } - } - } - ''' - - result = schema.execute(query) - assert len(result.errors) == 1 - assert str(result.errors[0]) == ( - 'Received two sliced querysets (high mark) in the connection, please slice only in one.' - ) From dbf006976efae85300255679138c6df8730a7583 Mon Sep 17 00:00:00 2001 From: Syrus Akbary Date: Sat, 15 Apr 2017 01:22:32 -0700 Subject: [PATCH 11/11] Removed unnecesary Django filter installation --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3c9b44b..6450bd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,6 @@ install: pip install -e .[test] pip install psycopg2 # Required for Django postgres fields testing pip install django==$DJANGO_VERSION - pip install django-filter==$DJANGO_FILTER_VERSION python setup.py develop elif [ "$TEST_TYPE" = lint ]; then pip install flake8