Skip to content

Commit 454b740

Browse files
tcleonardThomas Leonard
andauthored
Fix backward Relay pagination (#1046)
* Fix backward Relay pagination * linting Co-authored-by: Thomas Leonard <[email protected]>
1 parent 4c0c821 commit 454b740

File tree

2 files changed

+105
-7
lines changed

2 files changed

+105
-7
lines changed

graphene_django/fields.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -147,22 +147,23 @@ def resolve_connection(cls, connection, args, iterable, max_limit=None):
147147

148148
if isinstance(iterable, QuerySet):
149149
list_length = iterable.count()
150-
list_slice_length = (
151-
min(max_limit, list_length) if max_limit is not None else list_length
152-
)
153150
else:
154151
list_length = len(iterable)
155-
list_slice_length = (
156-
min(max_limit, list_length) if max_limit is not None else list_length
157-
)
152+
list_slice_length = (
153+
min(max_limit, list_length) if max_limit is not None else list_length
154+
)
158155

159156
# If after is higher than list_length, connection_from_list_slice
160157
# would try to do a negative slicing which makes django throw an
161158
# AssertionError
162159
after = min(get_offset_with_default(args.get("after"), -1) + 1, list_length)
163160

164161
if max_limit is not None and "first" not in args:
165-
args["first"] = max_limit
162+
if "last" in args:
163+
args["first"] = list_length
164+
list_slice_length = list_length
165+
else:
166+
args["first"] = max_limit
166167

167168
connection = connection_from_list_slice(
168169
iterable[after:],

graphene_django/tests/test_query.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,6 +1213,103 @@ class Query(graphene.ObjectType):
12131213
}
12141214

12151215

1216+
class TestBackwardPagination:
1217+
def setup_schema(self, graphene_settings, max_limit):
1218+
graphene_settings.RELAY_CONNECTION_MAX_LIMIT = max_limit
1219+
reporters = [Reporter(**kwargs) for kwargs in REPORTERS]
1220+
Reporter.objects.bulk_create(reporters)
1221+
1222+
class ReporterType(DjangoObjectType):
1223+
class Meta:
1224+
model = Reporter
1225+
interfaces = (Node,)
1226+
1227+
class Query(graphene.ObjectType):
1228+
all_reporters = DjangoConnectionField(ReporterType)
1229+
1230+
schema = graphene.Schema(query=Query)
1231+
return schema
1232+
1233+
def do_queries(self, schema):
1234+
# Simply last 3
1235+
query_last = """
1236+
query {
1237+
allReporters(last: 3) {
1238+
edges {
1239+
node {
1240+
firstName
1241+
}
1242+
}
1243+
}
1244+
}
1245+
"""
1246+
1247+
result = schema.execute(query_last)
1248+
assert not result.errors
1249+
assert len(result.data["allReporters"]["edges"]) == 3
1250+
assert [
1251+
e["node"]["firstName"] for e in result.data["allReporters"]["edges"]
1252+
] == ["First 3", "First 4", "First 5"]
1253+
1254+
# Use a combination of first and last
1255+
query_first_and_last = """
1256+
query {
1257+
allReporters(first: 4, last: 3) {
1258+
edges {
1259+
node {
1260+
firstName
1261+
}
1262+
}
1263+
}
1264+
}
1265+
"""
1266+
1267+
result = schema.execute(query_first_and_last)
1268+
assert not result.errors
1269+
assert len(result.data["allReporters"]["edges"]) == 3
1270+
assert [
1271+
e["node"]["firstName"] for e in result.data["allReporters"]["edges"]
1272+
] == ["First 1", "First 2", "First 3"]
1273+
1274+
# Use a combination of first and last and after
1275+
query_first_last_and_after = """
1276+
query queryAfter($after: String) {
1277+
allReporters(first: 4, last: 3, after: $after) {
1278+
edges {
1279+
node {
1280+
firstName
1281+
}
1282+
}
1283+
}
1284+
}
1285+
"""
1286+
1287+
after = base64.b64encode(b"arrayconnection:0").decode()
1288+
result = schema.execute(
1289+
query_first_last_and_after, variable_values=dict(after=after)
1290+
)
1291+
assert not result.errors
1292+
assert len(result.data["allReporters"]["edges"]) == 3
1293+
assert [
1294+
e["node"]["firstName"] for e in result.data["allReporters"]["edges"]
1295+
] == ["First 2", "First 3", "First 4"]
1296+
1297+
def test_should_query(self, graphene_settings):
1298+
"""
1299+
Backward pagination should work as expected
1300+
"""
1301+
schema = self.setup_schema(graphene_settings, max_limit=100)
1302+
self.do_queries(schema)
1303+
1304+
def test_should_query_with_low_max_limit(self, graphene_settings):
1305+
"""
1306+
When doing backward pagination (using last) in combination with a max limit higher than the number of objects
1307+
we should really retrieve the last ones.
1308+
"""
1309+
schema = self.setup_schema(graphene_settings, max_limit=4)
1310+
self.do_queries(schema)
1311+
1312+
12161313
def test_should_preserve_prefetch_related(django_assert_num_queries):
12171314
class ReporterType(DjangoObjectType):
12181315
class Meta:

0 commit comments

Comments
 (0)