Improved Relay types

This commit is contained in:
Syrus Akbary 2015-10-16 00:34:09 -07:00
parent 633f72cfe9
commit bd0ec6dc14
9 changed files with 70 additions and 82 deletions

View File

@ -31,5 +31,3 @@ from graphene.core.fields import (
from graphene.decorators import (
resolve_only_args
)
# import graphene.relay

View File

@ -7,6 +7,7 @@ from graphene.core.fields import Field, LazyField
from graphene.utils import cached_property, memoize, LazyMap
from graphene.relay.types import BaseNode
from graphene.relay.utils import is_node
from graphene.contrib.django.utils import get_type_for_model, lazy_map
@ -28,7 +29,7 @@ class ConnectionOrListField(LazyField):
def get_field(self, schema):
model_field = self.field_type
field_object_type = model_field.get_object_type(schema)
if field_object_type and issubclass(field_object_type, BaseNode):
if is_node(field_object_type):
field = DjangoConnectionField(model_field)
else:
field = LazyListField(model_field)

View File

@ -4,6 +4,7 @@ from django.db import models
from graphene.core.options import Options
from graphene.core.types import BaseObjectType
from graphene.relay.utils import is_node
from graphene.relay.types import Node
VALID_ATTRS = ('model', 'only_fields', 'exclude_fields')
@ -26,6 +27,7 @@ class DjangoOptions(Options):
super(DjangoOptions, self).contribute_to_class(cls, name)
if is_node(cls):
self.exclude_fields += ['id']
self.interfaces.append(Node)
if not is_node(cls) and not is_base(cls):
return
if not self.model:

View File

@ -5,8 +5,8 @@ from graphene.core.types import ObjectTypeMeta, BaseObjectType
from graphene.contrib.django.options import DjangoOptions
from graphene.contrib.django.converter import convert_django_field
from graphene.relay.types import Node, BaseNode
from graphene.relay.fields import NodeIDField
from graphene.relay.types import BaseNode
from graphene.relay.fields import GlobalIDField
def get_reverse_fields(model):
@ -53,7 +53,7 @@ class DjangoInterface(six.with_metaclass(DjangoObjectTypeMeta, BaseObjectType)):
class DjangoNode(BaseNode, DjangoInterface):
id = NodeIDField()
id = GlobalIDField()
@classmethod
def get_node(cls, id):

View File

@ -1,10 +1,9 @@
from graphene.relay.fields import (
ConnectionField,
NodeField
NodeField,
GlobalIDField,
)
from graphene.relay.types import (
Node
)
from graphene.relay.types import Node
from graphene.relay.utils import is_node

View File

@ -7,13 +7,15 @@ from graphql_relay.connection.connection import (
connectionArgs
)
from graphql_relay.node.node import (
global_id_field,
to_global_id,
from_global_id
)
from graphql.core.type import (
GraphQLNonNull,
GraphQLID,
GraphQLArgument,
)
from graphene.core.fields import Field, LazyNativeField, IDField
from graphene.utils import cached_property
from graphene.core.fields import Field, IDField
from graphene.utils import memoize
@ -36,44 +38,48 @@ class ConnectionField(Field):
@memoize
def internal_type(self, schema):
from graphene.relay.types import BaseNode
from graphene.relay.utils import is_node
object_type = self.get_object_type(schema)
assert issubclass(
object_type, BaseNode), 'Only nodes have connections.'
assert is_node(object_type), 'Only nodes have connections.'
return object_type.get_connection(schema)
class NodeField(LazyNativeField):
class NodeField(Field):
def __init__(self, object_type=None, *args, **kwargs):
super(NodeField, self).__init__(*args, **kwargs)
from graphene.relay.types import Node
super(NodeField, self).__init__(object_type or Node, *args, **kwargs)
self.field_object_type = object_type
self.args['id'] = GraphQLArgument(
GraphQLNonNull(GraphQLID),
description='The ID of an object'
)
def get_object_type_field(self, schema):
from graphene.relay.types import BaseNode
node_field = BaseNode.get_definitions(schema).node_field
def resolver(instance, args, info):
global_id = args.get('id')
def id_fetcher(self, global_id, info):
from graphene.relay.utils import is_node
schema = info.schema.graphene_schema
resolved_global_id = from_global_id(global_id)
if resolved_global_id.type == self.field_object_type._meta.type_name:
return node_field.resolver(instance, args, info)
_type, _id = resolved_global_id.type, resolved_global_id.id
object_type = schema.get_type(_type)
if not is_node(object_type) or (self.field_object_type and
object_type != self.field_object_type):
return
args = OrderedDict(node_field.args.items())
field = Field(self.field_object_type, id=args['id'], resolve=resolver)
field.contribute_to_class(self.object_type, self.field_name)
return field.internal_field(schema)
def get_field(self, schema):
if self.field_object_type:
return self.get_object_type_field(schema)
from graphene.relay.types import BaseNode
return BaseNode.get_definitions(schema).node_field
class NodeIDField(IDField):
required = True
return object_type.get_node(_id)
def resolve(self, instance, args, info):
type_name = self.object_type._meta.type_name
return to_global_id(type_name, instance.id)
global_id = args.get('id')
return self.id_fetcher(global_id, info)
class GlobalIDField(IDField):
'''The ID of an object'''
required = True
def contribute_to_class(self, cls, name, add=True):
from graphene.relay.utils import is_node, is_node_type
in_node = is_node(cls) or is_node_type(cls)
assert in_node, 'GlobalIDField could only be inside a Node, but got %r' % cls
super(GlobalIDField, self).contribute_to_class(cls, name, add)
def resolve(self, instance, args, info):
return self.object_type.to_global_id(instance, args, info)

View File

@ -1,37 +1,16 @@
from graphql_relay.node.node import (
node_definitions,
from_global_id
to_global_id
)
from graphql_relay.connection.connection import (
connection_definitions
)
from graphene.core.types import Interface
from graphene.core.fields import LazyNativeField
from graphene.relay.fields import NodeIDField
from graphene.relay.fields import GlobalIDField
from graphene.utils import memoize
def get_node_type(schema, obj, info=None):
return obj.internal_type(schema)
def get_node(schema, global_id, *args):
resolved_global_id = from_global_id(global_id)
_type, _id = resolved_global_id.type, resolved_global_id.id
object_type = schema.get_type(_type)
if not object_type or not issubclass(object_type, BaseNode):
raise Exception("The type %s is not a Node" % _type)
return object_type.get_node(_id)
class BaseNode(object):
@classmethod
@memoize
def get_definitions(cls, schema):
return node_definitions(lambda *args: get_node(schema, *args), lambda *args: get_node_type(schema, *args))
@classmethod
@memoize
def get_connection(cls, schema):
@ -40,14 +19,6 @@ class BaseNode(object):
connection = connection_definitions(type_name, _type).connection_type
return connection
@classmethod
def internal_type(cls, schema):
from graphene.relay.utils import is_node_type
if is_node_type(cls):
# Return only node_interface when is the Node Inerface
return BaseNode.get_definitions(schema).node_interface
return super(BaseNode, cls).internal_type(schema)
@classmethod
def _prepare_class(cls):
from graphene.relay.utils import is_node
@ -55,6 +26,12 @@ class BaseNode(object):
assert hasattr(
cls, 'get_node'), 'get_node classmethod not found in %s Node' % cls
@classmethod
def to_global_id(cls, instance, args, info):
type_name = cls._meta.type_name
return to_global_id(type_name, instance.id)
class Node(BaseNode, Interface):
id = NodeIDField()
'''An object with an ID'''
id = GlobalIDField()

View File

@ -2,7 +2,7 @@ from graphene.relay.types import BaseNode
def is_node(object_type):
return issubclass(object_type, BaseNode) and not is_node_type(object_type)
return object_type and issubclass(object_type, BaseNode) and not is_node_type(object_type)
def is_node_type(object_type):

View File

@ -2,7 +2,7 @@ from py.test import raises
from collections import namedtuple
from pytest import raises
from graphene.relay.fields import (
NodeIDField
GlobalIDField
)
from graphene.core.fields import (
Field,
@ -62,12 +62,12 @@ def test_pseudo_interface():
def test_djangonode_idfield():
idfield = DjangoNode._meta.fields_map['id']
assert isinstance(idfield, NodeIDField)
assert isinstance(idfield, GlobalIDField)
def test_node_idfield():
idfield = Human._meta.fields_map['id']
assert isinstance(idfield, NodeIDField)
assert isinstance(idfield, GlobalIDField)
def test_node_replacedfield():
@ -95,4 +95,9 @@ def test_object_type():
# 'reporter': fields_map['reporter'].internal_field(schema),
# 'pubDate': fields_map['pub_date'].internal_field(schema),
# }
assert object_type.get_interfaces() == [DjangoNode.internal_type(schema)]
assert DjangoNode.internal_type(schema) in object_type.get_interfaces()
def test_node_notinterface():
assert Human._meta.interface is False
assert DjangoNode in Human._meta.interfaces