mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-10-26 05:31:05 +03:00 
			
		
		
		
	Improved Node get_node_from_global_id
This introduces a breaking changes for custom Nodes implementations
This commit is contained in:
		
							parent
							
								
									256c84a9a5
								
							
						
					
					
						commit
						e8fc58afd6
					
				|  | @ -55,8 +55,13 @@ Example of a custom node: | |||
|             return '{}:{}'.format(type, id) | ||||
| 
 | ||||
|         @staticmethod | ||||
|         def get_node_from_global_id(global_id, context, info): | ||||
|         def get_node_from_global_id(global_id, context, info, only_type=None): | ||||
|             type, id = global_id.split(':') | ||||
|             if only_node: | ||||
|                 # We assure that the node type that we want to retrieve | ||||
|                 # is the same that was indicated in the field type | ||||
|                 assert type == only_node._meta.name, 'Received not compatible node.' | ||||
| 
 | ||||
|             if type == 'User': | ||||
|                 return get_user(id) | ||||
|             elif type == 'Photo': | ||||
|  | @ -66,6 +71,17 @@ Example of a custom node: | |||
| The ``get_node_from_global_id`` method will be called when ``CustomNode.Field`` is resolved. | ||||
| 
 | ||||
| 
 | ||||
| Accessing node types | ||||
| -------------------- | ||||
| 
 | ||||
| If we want to retrieve node instances from a ``global_id`` (scalar that identifies an instance by it's type name and id), | ||||
| we can simply do ``Node.get_node_from_global_id(global_id, contet, info)``. | ||||
| 
 | ||||
| In the case we want to restric the instnance retrieval to an specific type, we can do: | ||||
| ``Node.get_node_from_global_id(global_id, contet, info, only_type=Ship)``. This will raise an error | ||||
| if the global_id doesn't correspond to a Ship type. | ||||
| 
 | ||||
| 
 | ||||
| Node Root field | ||||
| --------------- | ||||
| 
 | ||||
|  |  | |||
|  | @ -63,12 +63,16 @@ class NodeField(Field): | |||
|     def __init__(self, node, type=False, deprecation_reason=None, | ||||
|                  name=None, **kwargs): | ||||
|         assert issubclass(node, Node), 'NodeField can only operate in Nodes' | ||||
|         type = type or node | ||||
|         self.node_type = node | ||||
| 
 | ||||
|         # If we don's specify a type, the field type will be the node interface | ||||
|         field_type = type or node | ||||
| 
 | ||||
|         super(NodeField, self).__init__( | ||||
|             type, | ||||
|             field_type, | ||||
|             description='The ID of the object', | ||||
|             id=ID(required=True), | ||||
|             resolver=node.node_resolver | ||||
|             resolver=partial(node.node_resolver, only_type=type) | ||||
|         ) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -80,18 +84,26 @@ class Node(six.with_metaclass(NodeMeta, Interface)): | |||
|         return NodeField(cls, *args, **kwargs) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def node_resolver(cls, root, args, context, info): | ||||
|         return cls.get_node_from_global_id(args.get('id'), context, info) | ||||
|     def node_resolver(cls, root, args, context, info, only_type=None): | ||||
|         return cls.get_node_from_global_id(args.get('id'), context, info, only_type) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def get_node_from_global_id(cls, global_id, context, info): | ||||
|     def get_node_from_global_id(cls, global_id, context, info, only_type=None): | ||||
|         try: | ||||
|             _type, _id = cls.from_global_id(global_id) | ||||
|             graphene_type = info.schema.get_type(_type).graphene_type | ||||
|             # We make sure the ObjectType implements the "Node" interface | ||||
|             assert cls in graphene_type._meta.interfaces | ||||
|         except: | ||||
|             return None | ||||
| 
 | ||||
|         if only_type: | ||||
|             assert graphene_type == only_type, ( | ||||
|                 'Must receive an {} id.' | ||||
|             ).format(graphene_type._meta.name) | ||||
| 
 | ||||
|         # We make sure the ObjectType implements the "Node" interface | ||||
|         if cls not in graphene_type._meta.interfaces: | ||||
|             return None | ||||
| 
 | ||||
|         get_node = getattr(graphene_type, 'get_node', None) | ||||
|         if get_node: | ||||
|             return get_node(_id, context, info) | ||||
|  |  | |||
|  | @ -44,6 +44,7 @@ class MyOtherNode(SharedNodeFields, ObjectType): | |||
| class RootQuery(ObjectType): | ||||
|     first = String() | ||||
|     node = Node.Field() | ||||
|     only_node = Node.Field(MyNode) | ||||
| 
 | ||||
| schema = Schema(query=RootQuery, types=[MyNode, MyOtherNode]) | ||||
| 
 | ||||
|  | @ -63,7 +64,7 @@ def test_node_get_connection_dont_duplicate(): | |||
| 
 | ||||
| def test_node_query(): | ||||
|     executed = schema.execute( | ||||
|         '{ node(id:"%s") { ... on MyNode { name } } }' % to_global_id("MyNode", 1) | ||||
|         '{ node(id:"%s") { ... on MyNode { name } } }' % Node.to_global_id("MyNode", 1) | ||||
|     ) | ||||
|     assert not executed.errors | ||||
|     assert executed.data == {'node': {'name': '1'}} | ||||
|  | @ -86,6 +87,35 @@ def test_node_query_incorrect_id(): | |||
|     assert executed.data == {'node': None} | ||||
| 
 | ||||
| 
 | ||||
| def test_node_field(): | ||||
|     node_field = Node.Field() | ||||
|     assert node_field.type == Node | ||||
|     assert node_field.node_type == Node | ||||
| 
 | ||||
| 
 | ||||
| def test_node_field_custom(): | ||||
|     node_field = Node.Field(MyNode) | ||||
|     assert node_field.type == MyNode | ||||
|     assert node_field.node_type == Node | ||||
| 
 | ||||
| 
 | ||||
| def test_node_field_only_type(): | ||||
|     executed = schema.execute( | ||||
|         '{ onlyNode(id:"%s") { __typename, name } } ' % Node.to_global_id("MyNode", 1) | ||||
|     ) | ||||
|     assert not executed.errors | ||||
|     assert executed.data == {'onlyNode': {'__typename': 'MyNode', 'name': '1'}} | ||||
| 
 | ||||
| 
 | ||||
| def test_node_field_only_type_wrong(): | ||||
|     executed = schema.execute( | ||||
|         '{ onlyNode(id:"%s") { __typename, name } } ' % Node.to_global_id("MyOtherNode", 1) | ||||
|     ) | ||||
|     assert len(executed.errors) == 1 | ||||
|     assert str(executed.errors[0]) == 'Must receive an MyOtherNode id.' | ||||
|     assert executed.data == { 'onlyNode': None } | ||||
| 
 | ||||
| 
 | ||||
| def test_str_schema(): | ||||
|     assert str(schema) == """ | ||||
| schema { | ||||
|  | @ -111,5 +141,6 @@ interface Node { | |||
| type RootQuery { | ||||
|   first: String | ||||
|   node(id: ID!): Node | ||||
|   onlyNode(id: ID!): MyNode | ||||
| } | ||||
| """.lstrip() | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ class CustomNode(Node): | |||
|         return id | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def get_node_from_global_id(id, context, info): | ||||
|     def get_node_from_global_id(id, context, info, only_type=None): | ||||
|         assert info.schema == schema | ||||
|         if id in user_data: | ||||
|             return user_data.get(id) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user