diff --git a/graphene/types/objecttype.py b/graphene/types/objecttype.py index bedc5e76..1f0e5b8d 100644 --- a/graphene/types/objecttype.py +++ b/graphene/types/objecttype.py @@ -24,6 +24,7 @@ class ObjectTypeMeta(AbstractTypeMeta): name=name, description=trim_docstring(attrs.get('__doc__')), interfaces=(), + possible_types=(), default_resolver=None, local_fields=OrderedDict(), ) @@ -55,6 +56,11 @@ class ObjectTypeMeta(AbstractTypeMeta): cls = type.__new__(cls, name, bases, dict(attrs, _meta=options)) + assert not (options.possible_types and cls.is_type_of), ( + '{}.Meta.possible_types will cause type collision with {}.is_type_of. ' + 'Please use one or other.' + ).format(name, name) + for interface in options.interfaces: interface.implements(cls) diff --git a/graphene/types/tests/test_objecttype.py b/graphene/types/tests/test_objecttype.py index 8ccc0fac..8a1cf898 100644 --- a/graphene/types/tests/test_objecttype.py +++ b/graphene/types/tests/test_objecttype.py @@ -184,3 +184,27 @@ def test_generate_objecttype_description(): ''' assert MyObjectType._meta.description == "Documentation\n\nDocumentation line 2" + + +def test_objecttype_with_possible_types(): + class MyObjectType(ObjectType): + class Meta: + possible_types = (dict, ) + + assert MyObjectType._meta.possible_types == (dict, ) + + +def test_objecttype_with_possible_types_and_is_type_of_should_raise(): + with pytest.raises(AssertionError) as excinfo: + class MyObjectType(ObjectType): + class Meta: + possible_types = (dict, ) + + @classmethod + def is_type_of(cls, root, context, info): + return False + + assert str(excinfo.value) == ( + 'MyObjectType.Meta.possible_types will cause type collision with ' + 'MyObjectType.is_type_of. Please use one or other.' + ) diff --git a/graphene/types/tests/test_typemap.py b/graphene/types/tests/test_typemap.py index 974b633e..475d0905 100644 --- a/graphene/types/tests/test_typemap.py +++ b/graphene/types/tests/test_typemap.py @@ -183,3 +183,18 @@ def test_objecttype_camelcase_disabled(): assert foo_field.args == { 'bar_foo': GraphQLArgument(GraphQLString, out_name='bar_foo') } + + +def test_objecttype_with_possible_types(): + class MyObjectType(ObjectType): + '''Description''' + class Meta: + possible_types = (dict, ) + + foo_bar = String() + + typemap = TypeMap([MyObjectType]) + graphql_type = typemap['MyObjectType'] + assert graphql_type.is_type_of + assert graphql_type.is_type_of({}, None, None) is True + assert graphql_type.is_type_of(MyObjectType(), None, None) is False diff --git a/graphene/types/typemap.py b/graphene/types/typemap.py index 0f75c113..0b59afb5 100644 --- a/graphene/types/typemap.py +++ b/graphene/types/typemap.py @@ -52,6 +52,10 @@ def resolve_type(resolve_type_func, map, type_name, root, context, info): return _type +def is_type_of_from_possible_types(possible_types, root, context, info): + return isinstance(root, possible_types) + + class TypeMap(GraphQLTypeMap): def __init__(self, types, auto_camelcase=True, schema=None): @@ -153,12 +157,17 @@ class TypeMap(GraphQLTypeMap): interfaces.append(internal_type) return interfaces + if type._meta.possible_types: + is_type_of = partial(is_type_of_from_possible_types, type._meta.possible_types) + else: + is_type_of = type.is_type_of + return GrapheneObjectType( graphene_type=type, name=type._meta.name, description=type._meta.description, fields=partial(self.construct_fields_for_type, map, type), - is_type_of=type.is_type_of, + is_type_of=is_type_of, interfaces=interfaces )