Improved classtypes relay support

This commit is contained in:
Syrus Akbary 2015-12-02 22:47:37 -08:00
parent 8abcaff02b
commit 5b3000f734
4 changed files with 57 additions and 48 deletions

View File

@ -40,9 +40,6 @@ class Interface(six.with_metaclass(InterfaceMeta, ObjectType)):
@classmethod @classmethod
def internal_type(cls, schema): def internal_type(cls, schema):
if cls._meta.abstract:
raise Exception("Abstract Interfaces don't have a specific type.")
if not cls._meta.interface: if not cls._meta.interface:
return super(Interface, cls).internal_type(schema) return super(Interface, cls).internal_type(schema)

View File

@ -90,11 +90,5 @@ class GlobalIDField(Field):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(GlobalIDField, self).__init__(NonNull(ID()), *args, **kwargs) super(GlobalIDField, self).__init__(NonNull(ID()), *args, **kwargs)
def contribute_to_class(self, cls, name):
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)
def resolver(self, instance, args, info): def resolver(self, instance, args, info):
return instance.to_global_id() return instance.to_global_id()

View File

@ -1,4 +1,5 @@
import inspect import inspect
import six
import warnings import warnings
from collections import Iterable from collections import Iterable
from functools import wraps from functools import wraps
@ -6,8 +7,10 @@ from functools import wraps
from graphql_relay.connection.arrayconnection import connection_from_list from graphql_relay.connection.arrayconnection import connection_from_list
from graphql_relay.node.node import to_global_id from graphql_relay.node.node import to_global_id
from ..core.types import (Boolean, Field, InputObjectType, Interface, List, from ..core.classtypes import InputObjectType, Interface, Mutation, ObjectType
Mutation, ObjectType, String) from ..core.classtypes.mutation import MutationMeta
from ..core.classtypes.interface import InterfaceMeta
from ..core.types import Boolean, Field, List, String
from ..core.types.argument import ArgumentsGroup from ..core.types.argument import ArgumentsGroup
from ..core.types.definitions import NonNull from ..core.types.definitions import NonNull
from ..utils import memoize from ..utils import memoize
@ -83,33 +86,43 @@ class Connection(ObjectType):
return self._connection_data return self._connection_data
class BaseNode(object): class NodeMeta(InterfaceMeta):
def construct_get_node(cls):
get_node = getattr(cls, 'get_node', None)
assert get_node, 'get_node classmethod not found in %s Node' % cls
assert callable(get_node), 'get_node have to be callable'
args = 3
if isinstance(get_node, staticmethod):
args -= 1
@classmethod get_node_num_args = len(inspect.getargspec(get_node).args)
def _prepare_class(cls): if get_node_num_args < args:
from graphene.relay.utils import is_node warnings.warn("get_node will receive also the info arg"
if is_node(cls): " in future versions of graphene".format(cls.__name__),
get_node = getattr(cls, 'get_node') FutureWarning)
assert get_node, 'get_node classmethod not found in %s Node' % cls
assert callable(get_node), 'get_node have to be callable'
args = 3
if isinstance(get_node, staticmethod):
args -= 1
get_node_num_args = len(inspect.getargspec(get_node).args) @staticmethod
if get_node_num_args < args: @wraps(get_node)
warnings.warn("get_node will receive also the info arg" def wrapped_node(*node_args):
" in future versions of graphene".format(cls.__name__), if len(node_args) < args:
FutureWarning) node_args += (None, )
return get_node(*node_args[:-1])
@staticmethod setattr(cls, 'get_node', wrapped_node)
@wraps(get_node)
def wrapped_node(*node_args):
if len(node_args) < args:
node_args += (None, )
return get_node(*node_args[:-1])
setattr(cls, 'get_node', wrapped_node) def construct(cls, *args, **kwargs):
cls = super(NodeMeta, cls).construct(*args, **kwargs)
if not cls._meta.abstract:
cls.construct_get_node()
return cls
class Node(six.with_metaclass(NodeMeta, Interface)):
'''An object with an ID'''
id = GlobalIDField()
class Meta:
abstract = True
def to_global_id(self): def to_global_id(self):
type_name = self._meta.type_name type_name = self._meta.type_name
@ -127,27 +140,31 @@ class BaseNode(object):
return cls.edge_type return cls.edge_type
class Node(BaseNode, Interface):
'''An object with an ID'''
id = GlobalIDField()
class MutationInputType(InputObjectType): class MutationInputType(InputObjectType):
client_mutation_id = String(required=True) client_mutation_id = String(required=True)
class ClientIDMutation(Mutation): class RelayMutationMeta(MutationMeta):
client_mutation_id = String(required=True) def construct(cls, *args, **kwargs):
cls = super(RelayMutationMeta, cls).construct(*args, **kwargs)
if not cls._meta.abstract:
assert hasattr(
cls, 'mutate_and_get_payload'), 'You have to implement mutate_and_get_payload'
return cls
@classmethod def construct_arguments(cls, items):
def _construct_arguments(cls, items):
assert hasattr(
cls, 'mutate_and_get_payload'), 'You have to implement mutate_and_get_payload'
new_input_type = type('{}Input'.format( new_input_type = type('{}Input'.format(
cls._meta.type_name), (MutationInputType, ), items) cls._meta.type_name), (MutationInputType, ), items)
cls.add_to_class('input_type', new_input_type) cls.add_to_class('input_type', new_input_type)
return ArgumentsGroup(input=NonNull(new_input_type)) return ArgumentsGroup(input=NonNull(new_input_type))
class ClientIDMutation(six.with_metaclass(RelayMutationMeta, Mutation)):
client_mutation_id = String(required=True)
class Meta:
abstract = True
@classmethod @classmethod
def mutate(cls, instance, args, info): def mutate(cls, instance, args, info):
input = args.get('input') input = args.get('input')

View File

@ -1,10 +1,11 @@
from .types import BaseNode from .types import Node
def is_node(object_type): def is_node(object_type):
return object_type and issubclass( return object_type and issubclass(
object_type, BaseNode) and not is_node_type(object_type) object_type, Node) and not object_type._meta.abstract
def is_node_type(object_type): def is_node_type(object_type):
return BaseNode in object_type.__bases__ return object_type and issubclass(
object_type, Node) and object_type._meta.abstract