From 828e951c0ec415caa8c5df754e73a9a9acc4b9b2 Mon Sep 17 00:00:00 2001 From: Erik Wrede Date: Thu, 2 Mar 2023 22:36:07 +0100 Subject: [PATCH] refactor: make relay connections extendable --- graphene/relay/connection.py | 36 ++++++++++------- graphene/relay/tests/test_connection.py | 51 ++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 15 deletions(-) diff --git a/graphene/relay/connection.py b/graphene/relay/connection.py index 1a4684e5..8216842f 100644 --- a/graphene/relay/connection.py +++ b/graphene/relay/connection.py @@ -61,8 +61,9 @@ class Connection(ObjectType): abstract = True @classmethod - def __init_subclass_with_meta__(cls, node=None, name=None, **options): - _meta = ConnectionOptions(cls) + def __init_subclass_with_meta__(cls, node=None, name=None, _meta=None, **options): + if not _meta: + _meta = ConnectionOptions(cls) assert node, f"You have to provide a node in {cls.__name__}.Meta" assert isinstance(node, NonNull) or issubclass( node, (Scalar, Enum, ObjectType, Interface, Union, NonNull) @@ -92,19 +93,26 @@ class Connection(ObjectType): cls.Edge = edge options["name"] = name + _meta.node = node - _meta.fields = { - "page_info": Field( - PageInfo, - name="pageInfo", - required=True, - description="Pagination data for this connection.", - ), - "edges": Field( - NonNull(List(edge)), - description="Contains the nodes in this connection.", - ), - } + + if not _meta.fields: + _meta.fields = {} + + _meta.fields.update( + { + "page_info": Field( + PageInfo, + name="pageInfo", + required=True, + description="Pagination data for this connection.", + ), + "edges": Field( + NonNull(List(edge)), + description="Contains the nodes in this connection.", + ), + } + ) return super(Connection, cls).__init_subclass_with_meta__( _meta=_meta, **options ) diff --git a/graphene/relay/tests/test_connection.py b/graphene/relay/tests/test_connection.py index 4015f4b4..c7d67e5b 100644 --- a/graphene/relay/tests/test_connection.py +++ b/graphene/relay/tests/test_connection.py @@ -1,7 +1,7 @@ from pytest import raises from ...types import Argument, Field, Int, List, NonNull, ObjectType, Schema, String -from ..connection import Connection, ConnectionField, PageInfo +from ..connection import Connection, ConnectionField, PageInfo, ConnectionOptions from ..node import Node @@ -51,6 +51,55 @@ def test_connection_inherit_abstracttype(): assert list(fields) == ["page_info", "edges", "extra"] +def test_connection_extra_abstract_fields(): + class ConnectionWithNodes(Connection): + class Meta: + abstract = True + + @classmethod + def __init_subclass_with_meta__(cls, node=None, name=None, **options): + _meta = ConnectionOptions(cls) + + _meta.fields = { + "nodes": Field( + NonNull(List(node)), + description="Contains all the nodes in this connection.", + ), + } + + return super(ConnectionWithNodes, cls).__init_subclass_with_meta__( + node=node, name=name, _meta=_meta, **options + ) + + class MyObjectConnection(ConnectionWithNodes): + class Meta: + node = MyObject + + class Edge: + other = String() + + assert MyObjectConnection._meta.name == "MyObjectConnection" + fields = MyObjectConnection._meta.fields + assert list(fields) == ["nodes", "page_info", "edges"] + edge_field = fields["edges"] + pageinfo_field = fields["page_info"] + nodes_field = fields["nodes"] + + assert isinstance(edge_field, Field) + assert isinstance(edge_field.type, NonNull) + assert isinstance(edge_field.type.of_type, List) + assert edge_field.type.of_type.of_type == MyObjectConnection.Edge + + assert isinstance(pageinfo_field, Field) + assert isinstance(pageinfo_field.type, NonNull) + assert pageinfo_field.type.of_type == PageInfo + + assert isinstance(nodes_field, Field) + assert isinstance(nodes_field.type, NonNull) + assert isinstance(nodes_field.type.of_type, List) + assert nodes_field.type.of_type.of_type == MyObject + + def test_connection_name(): custom_name = "MyObjectCustomNameConnection"