mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-10-30 23:47:55 +03:00 
			
		
		
		
	Allow ConnectionFields to have ObjectTypes as per relay spec
This commit is contained in:
		
							parent
							
								
									18be1daaa4
								
							
						
					
					
						commit
						b193c98e35
					
				|  | @ -6,12 +6,15 @@ from .fields import ( | ||||||
| 
 | 
 | ||||||
| from .types import ( | from .types import ( | ||||||
|     Node, |     Node, | ||||||
|     PageInfo, |  | ||||||
|     Edge, |  | ||||||
|     Connection, |  | ||||||
|     ClientIDMutation |     ClientIDMutation | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | from .connection import ( | ||||||
|  |     PageInfo, | ||||||
|  |     Connection, | ||||||
|  |     Edge, | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| from .utils import is_node | from .utils import is_node | ||||||
| 
 | 
 | ||||||
| __all__ = ['ConnectionField', 'NodeField', 'GlobalIDField', 'Node', | __all__ = ['ConnectionField', 'NodeField', 'GlobalIDField', 'Node', | ||||||
|  |  | ||||||
							
								
								
									
										85
									
								
								graphene/relay/connection.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								graphene/relay/connection.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,85 @@ | ||||||
|  | from ..core.classtypes import ObjectType | ||||||
|  | from ..core.types import Field, Boolean, String, List | ||||||
|  | from ..utils import memoize | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class PageInfo(ObjectType): | ||||||
|  | 
 | ||||||
|  |     def __init__(self, start_cursor="", end_cursor="", | ||||||
|  |                  has_previous_page=False, has_next_page=False, **kwargs): | ||||||
|  |         super(PageInfo, self).__init__(**kwargs) | ||||||
|  |         self.startCursor = start_cursor | ||||||
|  |         self.endCursor = end_cursor | ||||||
|  |         self.hasPreviousPage = has_previous_page | ||||||
|  |         self.hasNextPage = has_next_page | ||||||
|  | 
 | ||||||
|  |     hasNextPage = Boolean( | ||||||
|  |         required=True, | ||||||
|  |         description='When paginating forwards, are there more items?') | ||||||
|  |     hasPreviousPage = Boolean( | ||||||
|  |         required=True, | ||||||
|  |         description='When paginating backwards, are there more items?') | ||||||
|  |     startCursor = String( | ||||||
|  |         description='When paginating backwards, the cursor to continue.') | ||||||
|  |     endCursor = String( | ||||||
|  |         description='When paginating forwards, the cursor to continue.') | ||||||
|  | 
 | ||||||
|  | class Edge(ObjectType): | ||||||
|  |     '''An edge in a connection.''' | ||||||
|  |     cursor = String( | ||||||
|  |         required=True, description='A cursor for use in pagination') | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     @memoize | ||||||
|  |     def for_node(cls, node): | ||||||
|  |         from graphene.relay.utils import is_node | ||||||
|  |         # assert is_node(node), 'ObjectTypes in a edge have to be Nodes' | ||||||
|  |         node_field = Field(node, description='The item at the end of the edge') | ||||||
|  |         return type( | ||||||
|  |             '%s%s' % (node._meta.type_name, cls._meta.type_name), | ||||||
|  |             (cls,), | ||||||
|  |             {'node_type': node, 'node': node_field}) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Connection(ObjectType): | ||||||
|  |     '''A connection to a list of items.''' | ||||||
|  | 
 | ||||||
|  |     def __init__(self, edges, page_info, **kwargs): | ||||||
|  |         super(Connection, self).__init__(**kwargs) | ||||||
|  |         self.edges = edges | ||||||
|  |         self.pageInfo = page_info | ||||||
|  | 
 | ||||||
|  |     class Meta: | ||||||
|  |         type_name = 'DefaultConnection' | ||||||
|  | 
 | ||||||
|  |     pageInfo = Field(PageInfo, required=True, | ||||||
|  |                      description='The Information to aid in pagination') | ||||||
|  | 
 | ||||||
|  |     _connection_data = None | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     @memoize | ||||||
|  |     def for_node(cls, node, edge_type=None): | ||||||
|  |         from graphene.relay.utils import is_node | ||||||
|  |         edge_type = edge_type or Edge.for_node(node) | ||||||
|  |         edges = List(edge_type, description='Information to aid in pagination.') | ||||||
|  |         return type( | ||||||
|  |             '%s%s' % (node._meta.type_name, cls._meta.type_name), | ||||||
|  |             (cls,), | ||||||
|  |             {'edge_type': edge_type, 'edges': edges}) | ||||||
|  | 
 | ||||||
|  |     @classmethod | ||||||
|  |     def from_list(cls, iterable, args, context, info): | ||||||
|  |         assert isinstance( | ||||||
|  |             iterable, Iterable), 'Resolved value from the connection field have to be iterable' | ||||||
|  |         connection = connection_from_list( | ||||||
|  |             iterable, args, connection_type=cls, | ||||||
|  |             edge_type=cls.edge_type, pageinfo_type=PageInfo) | ||||||
|  |         connection.set_connection_data(iterable) | ||||||
|  |         return connection | ||||||
|  | 
 | ||||||
|  |     def set_connection_data(self, data): | ||||||
|  |         self._connection_data = data | ||||||
|  | 
 | ||||||
|  |     def get_connection_data(self): | ||||||
|  |         return self._connection_data | ||||||
|  | @ -6,6 +6,7 @@ from ..core.fields import Field | ||||||
| from ..core.types.definitions import NonNull | from ..core.types.definitions import NonNull | ||||||
| from ..core.types.scalars import ID, Int, String | from ..core.types.scalars import ID, Int, String | ||||||
| from ..utils.wrap_resolver_function import has_context, with_context | from ..utils.wrap_resolver_function import has_context, with_context | ||||||
|  | from .connection import Connection, Edge | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ConnectionField(Field): | class ConnectionField(Field): | ||||||
|  | @ -45,19 +46,18 @@ class ConnectionField(Field): | ||||||
|         return connection_type.from_list(resolved, args, context, info) |         return connection_type.from_list(resolved, args, context, info) | ||||||
| 
 | 
 | ||||||
|     def get_connection_type(self, node): |     def get_connection_type(self, node): | ||||||
|         connection_type = self.connection_type or node.get_connection_type() |         connection_type = self.connection_type or Connection | ||||||
|         edge_type = self.get_edge_type(node) |         edge_type = self.get_edge_type(node) | ||||||
|         return connection_type.for_node(node, edge_type=edge_type) |         return connection_type.for_node(node, edge_type=edge_type) | ||||||
| 
 | 
 | ||||||
|     def get_edge_type(self, node): |     def get_edge_type(self, node): | ||||||
|         edge_type = self.edge_type or node.get_edge_type() |         edge_type = self.edge_type or Edge | ||||||
|         return edge_type.for_node(node) |         return edge_type.for_node(node) | ||||||
| 
 | 
 | ||||||
|     def get_type(self, schema): |     def get_type(self, schema): | ||||||
|         from graphene.relay.utils import is_node |         from graphene.relay.utils import is_node | ||||||
|         type = schema.T(self.type) |         type = schema.T(self.type) | ||||||
|         node = schema.objecttype(type) |         node = schema.objecttype(type) | ||||||
|         assert is_node(node), 'Only nodes have connections.' |  | ||||||
|         schema.register(node) |         schema.register(node) | ||||||
|         connection_type = self.get_connection_type(node) |         connection_type = self.get_connection_type(node) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -19,90 +19,6 @@ from ..utils.wrap_resolver_function import has_context, with_context | ||||||
| from .fields import GlobalIDField | from .fields import GlobalIDField | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class PageInfo(ObjectType): |  | ||||||
| 
 |  | ||||||
|     def __init__(self, start_cursor="", end_cursor="", |  | ||||||
|                  has_previous_page=False, has_next_page=False, **kwargs): |  | ||||||
|         super(PageInfo, self).__init__(**kwargs) |  | ||||||
|         self.startCursor = start_cursor |  | ||||||
|         self.endCursor = end_cursor |  | ||||||
|         self.hasPreviousPage = has_previous_page |  | ||||||
|         self.hasNextPage = has_next_page |  | ||||||
| 
 |  | ||||||
|     hasNextPage = Boolean( |  | ||||||
|         required=True, |  | ||||||
|         description='When paginating forwards, are there more items?') |  | ||||||
|     hasPreviousPage = Boolean( |  | ||||||
|         required=True, |  | ||||||
|         description='When paginating backwards, are there more items?') |  | ||||||
|     startCursor = String( |  | ||||||
|         description='When paginating backwards, the cursor to continue.') |  | ||||||
|     endCursor = String( |  | ||||||
|         description='When paginating forwards, the cursor to continue.') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class Edge(ObjectType): |  | ||||||
|     '''An edge in a connection.''' |  | ||||||
|     cursor = String( |  | ||||||
|         required=True, description='A cursor for use in pagination') |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     @memoize |  | ||||||
|     def for_node(cls, node): |  | ||||||
|         from graphene.relay.utils import is_node |  | ||||||
|         assert is_node(node), 'ObjectTypes in a edge have to be Nodes' |  | ||||||
|         node_field = Field(node, description='The item at the end of the edge') |  | ||||||
|         return type( |  | ||||||
|             '%s%s' % (node._meta.type_name, cls._meta.type_name), |  | ||||||
|             (cls,), |  | ||||||
|             {'node_type': node, 'node': node_field}) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class Connection(ObjectType): |  | ||||||
|     '''A connection to a list of items.''' |  | ||||||
| 
 |  | ||||||
|     def __init__(self, edges, page_info, **kwargs): |  | ||||||
|         super(Connection, self).__init__(**kwargs) |  | ||||||
|         self.edges = edges |  | ||||||
|         self.pageInfo = page_info |  | ||||||
| 
 |  | ||||||
|     class Meta: |  | ||||||
|         type_name = 'DefaultConnection' |  | ||||||
| 
 |  | ||||||
|     pageInfo = Field(PageInfo, required=True, |  | ||||||
|                      description='The Information to aid in pagination') |  | ||||||
| 
 |  | ||||||
|     _connection_data = None |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     @memoize |  | ||||||
|     def for_node(cls, node, edge_type=None): |  | ||||||
|         from graphene.relay.utils import is_node |  | ||||||
|         edge_type = edge_type or Edge.for_node(node) |  | ||||||
|         assert is_node(node), 'ObjectTypes in a connection have to be Nodes' |  | ||||||
|         edges = List(edge_type, description='Information to aid in pagination.') |  | ||||||
|         return type( |  | ||||||
|             '%s%s' % (node._meta.type_name, cls._meta.type_name), |  | ||||||
|             (cls,), |  | ||||||
|             {'edge_type': edge_type, 'edges': edges}) |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def from_list(cls, iterable, args, context, info): |  | ||||||
|         assert isinstance( |  | ||||||
|             iterable, Iterable), 'Resolved value from the connection field have to be iterable' |  | ||||||
|         connection = connection_from_list( |  | ||||||
|             iterable, args, connection_type=cls, |  | ||||||
|             edge_type=cls.edge_type, pageinfo_type=PageInfo) |  | ||||||
|         connection.set_connection_data(iterable) |  | ||||||
|         return connection |  | ||||||
| 
 |  | ||||||
|     def set_connection_data(self, data): |  | ||||||
|         self._connection_data = data |  | ||||||
| 
 |  | ||||||
|     def get_connection_data(self): |  | ||||||
|         return self._connection_data |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class NodeMeta(InterfaceMeta): | class NodeMeta(InterfaceMeta): | ||||||
| 
 | 
 | ||||||
|     def construct_get_node(cls): |     def construct_get_node(cls): | ||||||
|  | @ -153,17 +69,6 @@ class Node(six.with_metaclass(NodeMeta, Interface)): | ||||||
|     def to_global_id(self): |     def to_global_id(self): | ||||||
|         return self.global_id(self.id) |         return self.global_id(self.id) | ||||||
| 
 | 
 | ||||||
|     connection_type = Connection |  | ||||||
|     edge_type = Edge |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def get_connection_type(cls): |  | ||||||
|         return cls.connection_type |  | ||||||
| 
 |  | ||||||
|     @classmethod |  | ||||||
|     def get_edge_type(cls): |  | ||||||
|         return cls.edge_type |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| class MutationInputType(InputObjectType): | class MutationInputType(InputObjectType): | ||||||
|     clientMutationId = String(required=True) |     clientMutationId = String(required=True) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user