From 781d4dd4fc3decfb595dabe9a6125f6cf84c8edd Mon Sep 17 00:00:00 2001 From: Pyhroff Date: Sat, 24 Oct 2026 13:29:20 +0530 Subject: [PATCH] Support lazy type references for Connection.Meta.node (#1600) --- graphene/relay/connection.py | 35 ++++++++++++++++++++++--- graphene/relay/tests/test_connection.py | 31 ++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/graphene/relay/connection.py b/graphene/relay/connection.py index cc7d2da0..1319f848 100644 --- a/graphene/relay/connection.py +++ b/graphene/relay/connection.py @@ -5,9 +5,12 @@ from graphql_relay import connection_from_array +import inspect + from ..types import Boolean, Enum, Int, Interface, List, NonNull, Scalar, String, Union from ..types.field import Field from ..types.objecttype import ObjectType, ObjectTypeOptions +from ..types.utils import get_type from ..utils.thenables import maybe_thenable from .node import is_node, AbstractNode @@ -81,7 +84,15 @@ def page_info_adapter(startCursor, endCursor, hasPreviousPage, hasNextPage): class ConnectionOptions(ObjectTypeOptions): - node = None + _node = None + + @property + def node(self): + return get_type(self._node) + + @node.setter + def node(self, value): + self._node = value class Connection(ObjectType): @@ -95,11 +106,27 @@ def __init_subclass_with_meta__( 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) + assert ( + isinstance(node, (str, NonNull)) + or inspect.isfunction(node) + or isinstance(node, partial) + or ( + inspect.isclass(node) + and issubclass( + node, (Scalar, Enum, ObjectType, Interface, Union, NonNull) + ) + ) ), f'Received incompatible node "{node}" for Connection {cls.__name__}.' - base_name = re.sub("Connection$", "", name or cls.__name__) or node._meta.name + base_name = re.sub("Connection$", "", name or cls.__name__) or ( + node._meta.name + if hasattr(node, "_meta") + else ( + node.split(".")[-1] + if isinstance(node, str) + else get_type(node)._meta.name + ) + ) if not name: name = f"{base_name}Connection" diff --git a/graphene/relay/tests/test_connection.py b/graphene/relay/tests/test_connection.py index 9c8b89d1..336de62a 100644 --- a/graphene/relay/tests/test_connection.py +++ b/graphene/relay/tests/test_connection.py @@ -316,3 +316,34 @@ class Meta: node_field = edges_list_element_type.of_type._meta.fields["node"] assert isinstance(node_field.type, NonNull) + + +def test_connection_with_lazy_string_node(): + class MyLazyStringConnection(Connection): + class Meta: + node = "graphene.relay.tests.test_connection.MyObject" + + assert MyLazyStringConnection._meta.name == "MyLazyStringConnection" + assert MyLazyStringConnection._meta.node == MyObject + edge_fields = MyLazyStringConnection.Edge._meta.fields + assert edge_fields["node"].type == MyObject + + +def test_connection_with_lazy_callable_node(): + class MyLazyCallableConnection(Connection): + class Meta: + node = lambda: MyObject # noqa: E731 + + assert MyLazyCallableConnection._meta.name == "MyLazyCallableConnection" + assert MyLazyCallableConnection._meta.node == MyObject + edge_fields = MyLazyCallableConnection.Edge._meta.fields + assert edge_fields["node"].type == MyObject + + +def test_connection_named_connection_with_lazy_string_node(): + class Connection(globals()["Connection"]): + class Meta: + node = "graphene.relay.tests.test_connection.MyObject" + + assert Connection._meta.name == "MyObjectConnection" + assert Connection._meta.node == MyObject