Skip to content

Commit 84fcb3e

Browse files
committed
Used Graphene types for relay Connection and Edges
1 parent 752cd77 commit 84fcb3e

File tree

5 files changed

+89
-8
lines changed

5 files changed

+89
-8
lines changed

graphene/core/types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __new__(cls, name, bases, attrs):
3030
# If this isn't a subclass of Model, don't do anything special.
3131
return super_new(cls, name, bases, attrs)
3232

33-
module = attrs.pop('__module__')
33+
module = attrs.pop('__module__', None)
3434
doc = attrs.pop('__doc__', None)
3535
new_class = super_new(cls, name, bases, {
3636
'__module__': module,

graphene/relay/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
GlobalIDField,
55
)
66

7-
from graphene.relay.types import Node
7+
from graphene.relay.types import (
8+
Node,
9+
PageInfo,
10+
Edge,
11+
Connection
12+
)
813

914
from graphene.relay.utils import is_node

graphene/relay/fields.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,14 @@
2121

2222
class ConnectionField(Field):
2323

24-
def __init__(self, field_type, resolve=None, description=''):
24+
def __init__(self, field_type, resolve=None, description='', connection_type=None, edge_type=None, **kwargs):
25+
from graphene.relay.types import Connection, Edge
2526
super(ConnectionField, self).__init__(field_type, resolve=resolve,
26-
args=connectionArgs, description=description)
27+
args=connectionArgs, description=description, **kwargs)
28+
self.connection_type = connection_type or Connection
29+
self.edge_type = edge_type or Edge
30+
assert issubclass(self.connection_type, Connection), 'connection_type in %r must be a subclass of Connection' % self
31+
assert issubclass(self.edge_type, Edge), 'edge_type in %r must be a subclass of Edge' % self
2732

2833
def wrap_resolved(self, value, instance, args, info):
2934
return value
@@ -39,9 +44,12 @@ def resolve(self, instance, args, info):
3944
@memoize
4045
def internal_type(self, schema):
4146
from graphene.relay.utils import is_node
42-
object_type = self.get_object_type(schema)
43-
assert is_node(object_type), 'Only nodes have connections.'
44-
return object_type.get_connection(schema)
47+
node = self.get_object_type(schema)
48+
assert is_node(node), 'Only nodes have connections.'
49+
schema.register(node)
50+
edge_node_type = self.edge_type.for_node(node)
51+
connection_node_type = self.connection_type.for_node(node, edge_type=edge_node_type)
52+
return connection_node_type.internal_type(schema)
4553

4654

4755
class NodeField(Field):

graphene/relay/types.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
connection_definitions
66
)
77

8-
from graphene.core.types import Interface
8+
from graphene.core.types import Interface, ObjectType
9+
from graphene.core.fields import BooleanField, StringField, ListField, Field
910
from graphene.relay.fields import GlobalIDField
1011
from graphene.utils import memoize
1112

@@ -35,3 +36,37 @@ def to_global_id(cls, instance, args, info):
3536
class Node(BaseNode, Interface):
3637
'''An object with an ID'''
3738
id = GlobalIDField()
39+
40+
41+
class PageInfo(ObjectType):
42+
has_next_page = BooleanField(required=True, description='When paginating forwards, are there more items?')
43+
has_previous_page = BooleanField(required=True, description='When paginating backwards, are there more items?')
44+
start_cursor = StringField(description='When paginating backwards, the cursor to continue.')
45+
end_cursor = StringField(description='When paginating forwards, the cursor to continue.')
46+
47+
48+
class Edge(ObjectType):
49+
'''An edge in a connection.'''
50+
node = Field(lambda field: field.object_type.node_type, description='The item at the end of the edge')
51+
end_cursor = StringField(required=True, description='A cursor for use in pagination')
52+
53+
@classmethod
54+
@memoize
55+
def for_node(cls, node):
56+
from graphene.relay.utils import is_node
57+
assert is_node(node), 'ObjectTypes in a edge have to be Nodes'
58+
return type('%sEdge' % node._meta.type_name, (cls, ), {'node_type': node})
59+
60+
61+
class Connection(ObjectType):
62+
'''A connection to a list of items.'''
63+
page_info = Field(PageInfo, required=True, description='The Information to aid in pagination')
64+
edges = ListField(lambda field: field.object_type.edge_type, description='Information to aid in pagination.')
65+
66+
@classmethod
67+
@memoize
68+
def for_node(cls, node, edge_type=None):
69+
from graphene.relay.utils import is_node
70+
edge_type = edge_type or Edge
71+
assert is_node(node), 'ObjectTypes in a connection have to be Nodes'
72+
return type('%sConnection' % node._meta.type_name, (cls, ), {'edge_type': edge_type.for_node(node)})

tests/relay/test_relayfields.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
class MyType(object):
1414
name = 'my'
15+
arg = None
16+
17+
18+
class MyConnection(relay.Connection):
19+
my_custom_field = graphene.StringField(resolve=lambda instance, *_: 'Custom')
1520

1621

1722
class MyNode(relay.Node):
@@ -24,7 +29,13 @@ def get_node(cls, id):
2429

2530
class Query(graphene.ObjectType):
2631
my_node = relay.NodeField(MyNode)
32+
all_my_nodes = relay.ConnectionField(MyNode, connection_type=MyConnection, customArg=graphene.Argument(graphene.String))
2733

34+
def resolve_all_my_nodes(self, args, info):
35+
t = MyType()
36+
custom_arg = args.get('customArg')
37+
assert custom_arg == "1"
38+
return [MyNode(t)]
2839

2940
schema.query = Query
3041

@@ -34,12 +45,34 @@ def test_nodefield_query():
3445
query RebelsShipsQuery {
3546
myNode(id:"TXlOb2RlOjE=") {
3647
name
48+
},
49+
allMyNodes (customArg:"1") {
50+
edges {
51+
node {
52+
name
53+
}
54+
},
55+
myCustomField
56+
pageInfo {
57+
hasNextPage
58+
}
3759
}
3860
}
3961
'''
4062
expected = {
4163
'myNode': {
4264
'name': 'my'
65+
},
66+
'allMyNodes': {
67+
'edges': [{
68+
'node': {
69+
'name': 'my'
70+
}
71+
}],
72+
'myCustomField': 'Custom',
73+
'pageInfo': {
74+
'hasNextPage': False,
75+
}
4376
}
4477
}
4578
result = schema.execute(query)

0 commit comments

Comments
 (0)