From 2aeb86ba3bed7dc821cc8508e1dca8297aa7dea4 Mon Sep 17 00:00:00 2001 From: Thomas Leonard <64223923+tcleonard@users.noreply.github.com> Date: Tue, 6 Sep 2022 14:00:13 +0200 Subject: [PATCH] fix: backward pagination indexing error when using bigger last argument than total number of elements (#1344) Co-authored-by: Thomas Leonard --- graphene_django/fields.py | 24 ++++----- graphene_django/tests/test_query.py | 75 +++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/graphene_django/fields.py b/graphene_django/fields.py index eb932c1..c881456 100644 --- a/graphene_django/fields.py +++ b/graphene_django/fields.py @@ -146,36 +146,38 @@ class DjangoConnectionField(ConnectionField): iterable = maybe_queryset(iterable) if isinstance(iterable, QuerySet): - list_length = iterable.count() + array_length = iterable.count() else: - list_length = len(iterable) - list_slice_length = ( - min(max_limit, list_length) if max_limit is not None else list_length + array_length = len(iterable) + array_slice_length = ( + min(max_limit, array_length) if max_limit is not None else array_length ) # If after is higher than list_length, connection_from_list_slice # would try to do a negative slicing which makes django throw an # AssertionError - after = min(get_offset_with_default(args.get("after"), -1) + 1, list_length) + slice_start = min( + get_offset_with_default(args.get("after"), -1) + 1, array_length + ) if max_limit is not None and args.get("first", None) is None: if args.get("last", None) is not None: - after = list_length - args["last"] + slice_start = max(array_length - args["last"], 0) else: args["first"] = max_limit connection = connection_from_array_slice( - iterable[after:], + iterable[slice_start:], args, - slice_start=after, - array_length=list_length, - array_slice_length=list_slice_length, + slice_start=slice_start, + array_length=array_length, + array_slice_length=array_slice_length, connection_type=partial(connection_adapter, connection), edge_type=connection.Edge, page_info_type=page_info_adapter, ) connection.iterable = iterable - connection.length = list_length + connection.length = array_length return connection @classmethod diff --git a/graphene_django/tests/test_query.py b/graphene_django/tests/test_query.py index aabe19c..5cbf90e 100644 --- a/graphene_django/tests/test_query.py +++ b/graphene_django/tests/test_query.py @@ -1593,3 +1593,78 @@ def test_connection_should_allow_offset_filtering_with_after(): "allReporters": {"edges": [{"node": {"firstName": "Jane", "lastName": "Roe"}},]} } assert result.data == expected + + +def test_connection_should_succeed_if_last_higher_than_number_of_objects(): + class ReporterType(DjangoObjectType): + class Meta: + model = Reporter + interfaces = (Node,) + fields = "__all__" + + class Query(graphene.ObjectType): + all_reporters = DjangoConnectionField(ReporterType) + + schema = graphene.Schema(query=Query) + query = """ + query ReporterPromiseConnectionQuery ($last: Int) { + allReporters(last: $last) { + edges { + node { + firstName + lastName + } + } + } + } + """ + + result = schema.execute(query, variable_values=dict(last=2)) + assert not result.errors + expected = {"allReporters": {"edges": []}} + assert result.data == expected + + Reporter.objects.create(first_name="John", last_name="Doe") + Reporter.objects.create(first_name="Some", last_name="Guy") + Reporter.objects.create(first_name="Jane", last_name="Roe") + Reporter.objects.create(first_name="Some", last_name="Lady") + + result = schema.execute(query, variable_values=dict(last=2)) + assert not result.errors + expected = { + "allReporters": { + "edges": [ + {"node": {"firstName": "Jane", "lastName": "Roe"}}, + {"node": {"firstName": "Some", "lastName": "Lady"}}, + ] + } + } + assert result.data == expected + + result = schema.execute(query, variable_values=dict(last=4)) + assert not result.errors + expected = { + "allReporters": { + "edges": [ + {"node": {"firstName": "John", "lastName": "Doe"}}, + {"node": {"firstName": "Some", "lastName": "Guy"}}, + {"node": {"firstName": "Jane", "lastName": "Roe"}}, + {"node": {"firstName": "Some", "lastName": "Lady"}}, + ] + } + } + assert result.data == expected + + result = schema.execute(query, variable_values=dict(last=20)) + assert not result.errors + expected = { + "allReporters": { + "edges": [ + {"node": {"firstName": "John", "lastName": "Doe"}}, + {"node": {"firstName": "Some", "lastName": "Guy"}}, + {"node": {"firstName": "Jane", "lastName": "Roe"}}, + {"node": {"firstName": "Some", "lastName": "Lady"}}, + ] + } + } + assert result.data == expected