diff --git a/graphene/core/classtypes/__init__.py b/graphene/core/classtypes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/graphene/core/classtypes/base.py b/graphene/core/classtypes/base.py new file mode 100644 index 00000000..a662ec96 --- /dev/null +++ b/graphene/core/classtypes/base.py @@ -0,0 +1,109 @@ +from collections import OrderedDict +import inspect +import six + +from ..exceptions import SkipField +from .options import Options + + +class ClassTypeMeta(type): + options_class = Options + + def __new__(mcs, name, bases, attrs): + super_new = super(ClassTypeMeta, mcs).__new__ + + module = attrs.pop('__module__', None) + doc = attrs.pop('__doc__', None) + new_class = super_new(mcs, name, bases, { + '__module__': module, + '__doc__': doc + }) + attr_meta = attrs.pop('Meta', None) + if not attr_meta: + meta = getattr(new_class, 'Meta', None) + else: + meta = attr_meta + + new_class.add_to_class('_meta', new_class.get_options(meta)) + + return mcs.construct(new_class, bases, attrs) + + def get_options(cls, meta): + return cls.options_class(meta) + + def add_to_class(cls, name, value): + # We should call the contribute_to_class method only if it's bound + if not inspect.isclass(value) and hasattr( + value, 'contribute_to_class'): + value.contribute_to_class(cls, name) + else: + setattr(cls, name, value) + + def construct(cls, bases, attrs): + # Add all attributes to the class. + for obj_name, obj in attrs.items(): + cls.add_to_class(obj_name, obj) + return cls + + +class ClassType(six.with_metaclass(ClassTypeMeta)): + @classmethod + def internal_type(cls, schema): + raise NotImplementedError("Function internal_type not implemented in type {}".format(cls)) + + +class FieldsOptions(Options): + def __init__(self, *args, **kwargs): + super(FieldsOptions, self).__init__(*args, **kwargs) + self.local_fields = [] + + def add_field(self, field): + self.local_fields.append(field) + + @property + def fields(self): + return sorted(self.local_fields) + + @property + def fields_map(self): + return OrderedDict([(f.attname, f) for f in self.fields]) + + +class FieldsClassTypeMeta(ClassTypeMeta): + options_class = FieldsOptions + + +class FieldsClassType(six.with_metaclass(FieldsClassTypeMeta, ClassType)): + @classmethod + def fields_internal_types(cls, schema): + fields = [] + for field in cls._meta.fields: + try: + fields.append((field.name, schema.T(field))) + except SkipField: + continue + + return OrderedDict(fields) + +# class NamedClassType(ClassType): +# pass + + +# class UnionType(NamedClassType): +# class Meta: +# abstract = True + + +# class ObjectType(NamedClassType): +# class Meta: +# abstract = True + + +# class InputObjectType(NamedClassType): +# class Meta: +# abstract = True + + +# class Mutation(ObjectType): +# class Meta: +# abstract = True diff --git a/graphene/core/classtypes/inputobjecttype.py b/graphene/core/classtypes/inputobjecttype.py new file mode 100644 index 00000000..393173e8 --- /dev/null +++ b/graphene/core/classtypes/inputobjecttype.py @@ -0,0 +1,24 @@ +from functools import partial + +from graphql.core.type import GraphQLInputObjectType + +from .base import FieldsClassType + + +class InputObjectType(FieldsClassType): + class Meta: + abstract = True + + def __init__(self, *args, **kwargs): + raise Exception("An InputObjectType cannot be initialized") + + @classmethod + def internal_type(cls, schema): + if cls._meta.abstract: + raise Exception("Abstract InputObjectTypes don't have a specific type.") + + return GraphQLInputObjectType( + cls._meta.type_name, + description=cls._meta.description, + fields=partial(cls.fields_internal_types, schema), + ) diff --git a/graphene/core/classtypes/interface.py b/graphene/core/classtypes/interface.py new file mode 100644 index 00000000..f689c709 --- /dev/null +++ b/graphene/core/classtypes/interface.py @@ -0,0 +1,54 @@ +import six +from functools import partial + +from graphql.core.type import GraphQLInterfaceType + +from .base import FieldsClassTypeMeta +from .objecttype import ObjectType, ObjectTypeMeta + + +class InterfaceMeta(ObjectTypeMeta): + def construct(cls, bases, attrs): + if cls._meta.abstract or Interface in bases: + # Return Interface type + cls = FieldsClassTypeMeta.construct(cls, bases, attrs) + setattr(cls._meta, 'interface', True) + return cls + else: + # Return ObjectType class with all the inherited interfaces + cls = super(InterfaceMeta, cls).construct(bases, attrs) + for interface in bases: + is_interface = issubclass(interface, Interface) and getattr(interface._meta, 'interface', False) + if not is_interface: + continue + cls._meta.interfaces.append(interface) + return cls + + +class Interface(six.with_metaclass(InterfaceMeta, ObjectType)): + class Meta: + abstract = True + + def __init__(self, *args, **kwargs): + if self._meta.interface: + raise Exception("An interface cannot be initialized") + return super(Interface, self).__init__(*args, **kwargs) + + @classmethod + def _resolve_type(cls, schema, instance, *args): + return schema.T(instance.__class__) + + @classmethod + def internal_type(cls, schema): + if cls._meta.abstract: + raise Exception("Abstract Interfaces don't have a specific type.") + + if not cls._meta.interface: + return super(Interface, cls).internal_type(schema) + + return GraphQLInterfaceType( + cls._meta.type_name, + description=cls._meta.description, + resolve_type=partial(cls._resolve_type, schema), + fields=partial(cls.fields_internal_types, schema) + ) diff --git a/graphene/core/classtypes/mutation.py b/graphene/core/classtypes/mutation.py new file mode 100644 index 00000000..90aa5cf8 --- /dev/null +++ b/graphene/core/classtypes/mutation.py @@ -0,0 +1,30 @@ +import six + +from ..types.argument import ArgumentsGroup +from .objecttype import ObjectType, ObjectTypeMeta + + +class MutationMeta(ObjectTypeMeta): + def construct(cls, bases, attrs): + input_class = attrs.pop('Input', None) + if input_class: + items = dict(vars(input_class)) + items.pop('__dict__', None) + items.pop('__doc__', None) + items.pop('__module__', None) + items.pop('__weakref__', None) + cls.add_to_class('arguments', cls.construct_arguments(items)) + cls = super(MutationMeta, cls).construct(bases, attrs) + return cls + + def construct_arguments(cls, items): + return ArgumentsGroup(**items) + + +class Mutation(six.with_metaclass(MutationMeta, ObjectType)): + class Meta: + abstract = True + + @classmethod + def get_arguments(cls): + return cls.arguments diff --git a/graphene/core/classtypes/objecttype.py b/graphene/core/classtypes/objecttype.py new file mode 100644 index 00000000..93d2286e --- /dev/null +++ b/graphene/core/classtypes/objecttype.py @@ -0,0 +1,99 @@ +import six +from functools import partial + +from graphql.core.type import GraphQLObjectType + +from graphene import signals +from .base import FieldsOptions, FieldsClassType, FieldsClassTypeMeta +from .uniontype import UnionType + + +def is_objecttype(cls): + if not issubclass(cls, ObjectType): + return False + return not cls._meta.interface + + +class ObjectTypeOptions(FieldsOptions): + def __init__(self, *args, **kwargs): + super(ObjectTypeOptions, self).__init__(*args, **kwargs) + self.interface = False + self.interfaces = [] + + +class ObjectTypeMeta(FieldsClassTypeMeta): + def construct(cls, bases, attrs): + cls = super(ObjectTypeMeta, cls).construct(bases, attrs) + if not cls._meta.abstract: + union_types = list(filter(is_objecttype, bases)) + if len(union_types) > 1: + meta_attrs = dict(cls._meta.original_attrs, types=union_types) + Meta = type('Meta', (object, ), meta_attrs) + attrs['Meta'] = Meta + attrs['__module__'] = cls.__module__ + attrs['__doc__'] = cls.__doc__ + return type(cls.__name__, (UnionType, ), attrs) + return cls + + options_class = ObjectTypeOptions + + +class ObjectType(six.with_metaclass(ObjectTypeMeta, FieldsClassType)): + class Meta: + abstract = True + + def __init__(self, *args, **kwargs): + signals.pre_init.send(self.__class__, args=args, kwargs=kwargs) + self._root = kwargs.pop('_root', None) + args_len = len(args) + fields = self._meta.fields + if args_len > len(fields): + # Daft, but matches old exception sans the err msg. + raise IndexError("Number of args exceeds number of fields") + fields_iter = iter(fields) + + if not kwargs: + for val, field in zip(args, fields_iter): + setattr(self, field.attname, val) + else: + for val, field in zip(args, fields_iter): + setattr(self, field.attname, val) + kwargs.pop(field.attname, None) + + for field in fields_iter: + try: + val = kwargs.pop(field.attname) + setattr(self, field.attname, val) + except KeyError: + pass + + if kwargs: + for prop in list(kwargs): + try: + if isinstance(getattr(self.__class__, prop), property): + setattr(self, prop, kwargs.pop(prop)) + except AttributeError: + pass + if kwargs: + raise TypeError( + "'%s' is an invalid keyword argument for this function" % + list(kwargs)[0]) + + signals.post_init.send(self.__class__, instance=self) + + @classmethod + def internal_type(cls, schema): + if cls._meta.abstract: + raise Exception("Abstract ObjectTypes don't have a specific type.") + + return GraphQLObjectType( + cls._meta.type_name, + description=cls._meta.description, + interfaces=[schema.T(i) for i in cls._meta.interfaces], + fields=partial(cls.fields_internal_types, schema), + is_type_of=getattr(cls, 'is_type_of', None) + ) + + @classmethod + def wrap(cls, instance, args, info): + return cls(_root=instance) diff --git a/graphene/core/classtypes/options.py b/graphene/core/classtypes/options.py new file mode 100644 index 00000000..7a965a2c --- /dev/null +++ b/graphene/core/classtypes/options.py @@ -0,0 +1,47 @@ +class Options(object): + def __init__(self, meta=None, **defaults): + self.meta = meta + self.abstract = False + for name, value in defaults.items(): + setattr(self, name, value) + self.valid_attrs = list(defaults.keys()) + ['type_name', 'description', 'abstract'] + + def contribute_to_class(self, cls, name): + cls._meta = self + self.parent = cls + # First, construct the default values for these options. + self.object_name = cls.__name__ + self.type_name = self.object_name + + self.description = cls.__doc__ + # Store the original user-defined values for each option, + # for use when serializing the model definition + self.original_attrs = {} + + # Next, apply any overridden values from 'class Meta'. + if self.meta: + meta_attrs = self.meta.__dict__.copy() + for name in self.meta.__dict__: + # Ignore any private attributes that Django doesn't care about. + # NOTE: We can't modify a dictionary's contents while looping + # over it, so we loop over the *original* dictionary instead. + if name.startswith('_'): + del meta_attrs[name] + for attr_name in self.valid_attrs: + if attr_name in meta_attrs: + setattr(self, attr_name, meta_attrs.pop(attr_name)) + self.original_attrs[attr_name] = getattr(self, attr_name) + elif hasattr(self.meta, attr_name): + setattr(self, attr_name, getattr(self.meta, attr_name)) + self.original_attrs[attr_name] = getattr(self, attr_name) + + del self.valid_attrs + + # Any leftover attributes must be invalid. + if meta_attrs != {}: + raise TypeError( + "'class Meta' got invalid attribute(s): %s" % + ','.join( + meta_attrs.keys())) + + del self.meta diff --git a/graphene/core/classtypes/scalar.py b/graphene/core/classtypes/scalar.py new file mode 100644 index 00000000..fe4087e8 --- /dev/null +++ b/graphene/core/classtypes/scalar.py @@ -0,0 +1,21 @@ +from graphql.core.type import GraphQLScalarType + +from .base import ClassType +from ..types.base import MountedType + + +class Scalar(ClassType, MountedType): + + @classmethod + def internal_type(cls, schema): + serialize = getattr(cls, 'serialize') + parse_literal = getattr(cls, 'parse_literal') + parse_value = getattr(cls, 'parse_value') + + return GraphQLScalarType( + name=cls._meta.type_name, + description=cls._meta.description, + serialize=serialize, + parse_value=parse_value, + parse_literal=parse_literal + ) diff --git a/graphene/core/classtypes/tests/__init__.py b/graphene/core/classtypes/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/graphene/core/classtypes/tests/test_base.py b/graphene/core/classtypes/tests/test_base.py new file mode 100644 index 00000000..2830b9bb --- /dev/null +++ b/graphene/core/classtypes/tests/test_base.py @@ -0,0 +1,40 @@ +from ..base import ClassType, FieldsClassType +from ...types import Field, String +from ...schema import Schema + + +def test_classtype_basic(): + class Character(ClassType): + '''Character description''' + pass + assert Character._meta.type_name == 'Character' + assert Character._meta.description == 'Character description' + + +def test_classtype_advanced(): + class Character(ClassType): + class Meta: + type_name = 'OtherCharacter' + description = 'OtherCharacter description' + assert Character._meta.type_name == 'OtherCharacter' + assert Character._meta.description == 'OtherCharacter description' + + +def test_fieldsclasstype(): + f = Field(String()) + + class Character(FieldsClassType): + field_name = f + + assert Character._meta.fields == [f] + + +def test_fieldsclasstype_fieldtype(): + f = Field(String()) + + class Character(FieldsClassType): + field_name = f + + schema = Schema(query=Character) + assert Character.fields_internal_types(schema)['fieldName'] == schema.T(f) + assert Character._meta.fields_map['field_name'] == f diff --git a/graphene/core/classtypes/tests/test_inputobjecttype.py b/graphene/core/classtypes/tests/test_inputobjecttype.py new file mode 100644 index 00000000..67306bc3 --- /dev/null +++ b/graphene/core/classtypes/tests/test_inputobjecttype.py @@ -0,0 +1,21 @@ +from py.test import raises + +from graphql.core.type import GraphQLInputObjectType + +from graphene.core.schema import Schema +from graphene.core.types import String +from ..inputobjecttype import InputObjectType + + +def test_inputobjecttype(): + class InputCharacter(InputObjectType): + '''InputCharacter description''' + name = String() + + schema = Schema() + + object_type = schema.T(InputCharacter) + assert isinstance(object_type, GraphQLInputObjectType) + assert InputCharacter._meta.type_name == 'InputCharacter' + assert object_type.description == 'InputCharacter description' + assert list(object_type.get_fields().keys()) == ['name'] diff --git a/graphene/core/classtypes/tests/test_interface.py b/graphene/core/classtypes/tests/test_interface.py new file mode 100644 index 00000000..43e9e503 --- /dev/null +++ b/graphene/core/classtypes/tests/test_interface.py @@ -0,0 +1,85 @@ +from py.test import raises + +from graphql.core.type import GraphQLInterfaceType, GraphQLObjectType + +from graphene.core.schema import Schema +from graphene.core.types import String +from ..interface import Interface +from ..objecttype import ObjectType + + +def test_interface(): + class Character(Interface): + '''Character description''' + name = String() + + schema = Schema() + + object_type = schema.T(Character) + assert issubclass(Character, Interface) + assert isinstance(object_type, GraphQLInterfaceType) + assert Character._meta.interface + assert Character._meta.type_name == 'Character' + assert object_type.description == 'Character description' + assert list(object_type.get_fields().keys()) == ['name'] + + +def test_interface_cannot_initialize(): + class Character(Interface): + pass + + with raises(Exception) as excinfo: + Character() + assert 'An interface cannot be initialized' == str(excinfo.value) + + +def test_interface_inheritance_abstract(): + class Character(Interface): + pass + + class ShouldBeInterface(Character): + class Meta: + abstract = True + + class ShouldBeObjectType(ShouldBeInterface): + pass + + assert ShouldBeInterface._meta.interface + assert not ShouldBeObjectType._meta.interface + assert issubclass(ShouldBeObjectType, ObjectType) + + +def test_interface_inheritance(): + class Character(Interface): + pass + + class GeneralInterface(Interface): + pass + + class ShouldBeObjectType(GeneralInterface, Character): + pass + + schema = Schema() + + assert Character._meta.interface + assert not ShouldBeObjectType._meta.interface + assert issubclass(ShouldBeObjectType, ObjectType) + assert Character in ShouldBeObjectType._meta.interfaces + assert GeneralInterface in ShouldBeObjectType._meta.interfaces + assert isinstance(schema.T(Character), GraphQLInterfaceType) + assert isinstance(schema.T(ShouldBeObjectType), GraphQLObjectType) + + +def test_interface_inheritance_non_objects(): + class ComonClass(object): + common_attr = True + + class Character(ComonClass, Interface): + pass + + class ShouldBeObjectType(Character): + pass + + assert Character._meta.interface + assert Character.common_attr + assert ShouldBeObjectType.common_attr diff --git a/graphene/core/classtypes/tests/test_mutation.py b/graphene/core/classtypes/tests/test_mutation.py new file mode 100644 index 00000000..ded164ea --- /dev/null +++ b/graphene/core/classtypes/tests/test_mutation.py @@ -0,0 +1,27 @@ +from py.test import raises + +from graphql.core.type import GraphQLObjectType + +from graphene.core.schema import Schema +from graphene.core.types import String +from ..mutation import Mutation +from ...types.argument import ArgumentsGroup + + +def test_mutation(): + class MyMutation(Mutation): + '''MyMutation description''' + class Input: + arg_name = String() + name = String() + + schema = Schema() + + object_type = schema.T(MyMutation) + assert MyMutation._meta.type_name == 'MyMutation' + assert isinstance(object_type, GraphQLObjectType) + assert object_type.description == 'MyMutation description' + assert list(object_type.get_fields().keys()) == ['name'] + assert MyMutation._meta.fields_map['name'].object_type == MyMutation + assert isinstance(MyMutation.arguments, ArgumentsGroup) + assert 'argName' in MyMutation.arguments diff --git a/graphene/core/classtypes/tests/test_objecttype.py b/graphene/core/classtypes/tests/test_objecttype.py new file mode 100644 index 00000000..95134bef --- /dev/null +++ b/graphene/core/classtypes/tests/test_objecttype.py @@ -0,0 +1,89 @@ +from py.test import raises + +from graphql.core.type import GraphQLObjectType + +from graphene.core.schema import Schema +from graphene.core.types import Int, String +from ..objecttype import ObjectType +from ..uniontype import UnionType + + +def test_object_type(): + class Human(ObjectType): + '''Human description''' + name = String() + friends = String() + + schema = Schema() + + object_type = schema.T(Human) + assert Human._meta.type_name == 'Human' + assert isinstance(object_type, GraphQLObjectType) + assert object_type.description == 'Human description' + assert list(object_type.get_fields().keys()) == ['name', 'friends'] + assert Human._meta.fields_map['name'].object_type == Human + + +def test_object_type_container(): + class Human(ObjectType): + name = String() + friends = String() + + h = Human(name='My name') + assert h.name == 'My name' + + +def test_object_type_set_properties(): + class Human(ObjectType): + name = String() + friends = String() + + @property + def readonly_prop(self): + return 'readonly' + + @property + def write_prop(self): + return self._write_prop + + @write_prop.setter + def write_prop(self, value): + self._write_prop = value + + h = Human(readonly_prop='custom', write_prop='custom') + assert h.readonly_prop == 'readonly' + assert h.write_prop == 'custom' + + +def test_object_type_container_invalid_kwarg(): + class Human(ObjectType): + name = String() + + with raises(TypeError): + Human(invalid='My name') + + +def test_object_type_container_too_many_args(): + class Human(ObjectType): + name = String() + + with raises(IndexError): + Human('Peter', 'No friends :(', None) + + +def test_object_type_union(): + class Human(ObjectType): + name = String() + + class Pet(ObjectType): + name = String() + + class Thing(Human, Pet): + '''Thing union description''' + my_attr = True + + assert issubclass(Thing, UnionType) + assert Thing._meta.types == [Human, Pet] + assert Thing._meta.type_name == 'Thing' + assert Thing._meta.description == 'Thing union description' + assert Thing.my_attr diff --git a/graphene/core/classtypes/tests/test_scalar.py b/graphene/core/classtypes/tests/test_scalar.py new file mode 100644 index 00000000..a6e37881 --- /dev/null +++ b/graphene/core/classtypes/tests/test_scalar.py @@ -0,0 +1,32 @@ +from graphql.core.type import GraphQLScalarType + +from ...schema import Schema +from ..scalar import Scalar + + +def test_custom_scalar(): + import datetime + from graphql.core.language import ast + + class DateTimeScalar(Scalar): + '''DateTimeScalar Documentation''' + @staticmethod + def serialize(dt): + return dt.isoformat() + + @staticmethod + def parse_literal(node): + if isinstance(node, ast.StringValue): + return datetime.datetime.strptime( + node.value, "%Y-%m-%dT%H:%M:%S.%f") + + @staticmethod + def parse_value(value): + return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f") + + schema = Schema() + + scalar_type = schema.T(DateTimeScalar) + assert isinstance(scalar_type, GraphQLScalarType) + assert scalar_type.name == 'DateTimeScalar' + assert scalar_type.description == 'DateTimeScalar Documentation' diff --git a/graphene/core/classtypes/tests/test_uniontype.py b/graphene/core/classtypes/tests/test_uniontype.py new file mode 100644 index 00000000..d19615fa --- /dev/null +++ b/graphene/core/classtypes/tests/test_uniontype.py @@ -0,0 +1,27 @@ +from graphql.core.type import GraphQLUnionType + +from graphene.core.schema import Schema +from graphene.core.types import String +from ..objecttype import ObjectType +from ..uniontype import UnionType + + +def test_uniontype(): + class Human(ObjectType): + name = String() + + class Pet(ObjectType): + name = String() + + class Thing(UnionType): + '''Thing union description''' + class Meta: + types = [Human, Pet] + + schema = Schema() + + object_type = schema.T(Thing) + assert isinstance(object_type, GraphQLUnionType) + assert Thing._meta.type_name == 'Thing' + assert object_type.description == 'Thing union description' + assert object_type.get_possible_types() == [schema.T(Human), schema.T(Pet)] diff --git a/graphene/core/classtypes/uniontype.py b/graphene/core/classtypes/uniontype.py new file mode 100644 index 00000000..c1fb86e3 --- /dev/null +++ b/graphene/core/classtypes/uniontype.py @@ -0,0 +1,39 @@ +import six + +from graphql.core.type import GraphQLUnionType + +from .base import FieldsOptions, FieldsClassType, FieldsClassTypeMeta + + +class UnionTypeOptions(FieldsOptions): + def __init__(self, *args, **kwargs): + super(UnionTypeOptions, self).__init__(*args, **kwargs) + self.types = [] + + +class UnionTypeMeta(FieldsClassTypeMeta): + options_class = UnionTypeOptions + + def get_options(cls, meta): + return cls.options_class(meta, types=[]) + + +class UnionType(six.with_metaclass(UnionTypeMeta, FieldsClassType)): + class Meta: + abstract = True + + @classmethod + def _resolve_type(cls, schema, instance, *args): + return schema.T(instance.__class__) + + @classmethod + def internal_type(cls, schema): + if cls._meta.abstract: + raise Exception("Abstract ObjectTypes don't have a specific type.") + + return GraphQLUnionType( + cls._meta.type_name, + types=map(schema.T, cls._meta.types), + resolve_type=cls._resolve_type, + description=cls._meta.description, + ) diff --git a/graphene/core/schema.py b/graphene/core/schema.py index eef79678..ec9d1bf5 100644 --- a/graphene/core/schema.py +++ b/graphene/core/schema.py @@ -12,6 +12,7 @@ from graphene import signals from .types.base import BaseType from .types.objecttype import BaseObjectType +from .classtypes.base import ClassType class GraphQLSchema(_GraphQLSchema): @@ -42,7 +43,7 @@ class Schema(object): if not object_type: return if inspect.isclass(object_type) and issubclass( - object_type, BaseType) or isinstance( + object_type, (BaseType, ClassType)) or isinstance( object_type, BaseType): if object_type not in self._types: internal_type = object_type.internal_type(self) diff --git a/graphene/core/types/base.py b/graphene/core/types/base.py index fe261790..2b9a4e01 100644 --- a/graphene/core/types/base.py +++ b/graphene/core/types/base.py @@ -2,6 +2,9 @@ from functools import total_ordering import six +from ..classtypes.base import FieldsClassType +from ..classtypes.inputobjecttype import InputObjectType as NewInputObjectType + class BaseType(object): @@ -105,10 +108,10 @@ class FieldType(MirroredType): def contribute_to_class(self, cls, name): from ..types import BaseObjectType, InputObjectType - if issubclass(cls, InputObjectType): + if issubclass(cls, (InputObjectType, NewInputObjectType)): inputfield = self.as_inputfield() return inputfield.contribute_to_class(cls, name) - elif issubclass(cls, BaseObjectType): + elif issubclass(cls, (BaseObjectType, FieldsClassType)): field = self.as_field() return field.contribute_to_class(cls, name) diff --git a/graphene/core/types/field.py b/graphene/core/types/field.py index db52086a..489516e1 100644 --- a/graphene/core/types/field.py +++ b/graphene/core/types/field.py @@ -5,6 +5,8 @@ import six from graphql.core.type import GraphQLField, GraphQLInputObjectField from ...utils import to_camel_case +from ..classtypes.base import FieldsClassType +from ..classtypes.inputobjecttype import InputObjectType as NewInputObjectType from ..types import BaseObjectType, InputObjectType from .argument import ArgumentsGroup, snake_case_args from .base import LazyType, MountType, OrderedType @@ -32,7 +34,7 @@ class Field(OrderedType): def contribute_to_class(self, cls, attname): assert issubclass( - cls, BaseObjectType), 'Field {} cannot be mounted in {}'.format( + cls, (BaseObjectType, FieldsClassType)), 'Field {} cannot be mounted in {}'.format( self, cls) if not self.name: self.name = to_camel_case(attname) @@ -126,7 +128,7 @@ class InputField(OrderedType): def contribute_to_class(self, cls, attname): assert issubclass( - cls, InputObjectType), 'InputField {} cannot be mounted in {}'.format( + cls, (InputObjectType, NewInputObjectType)), 'InputField {} cannot be mounted in {}'.format( self, cls) if not self.name: self.name = to_camel_case(attname)