Added support for UnionTypes

This commit is contained in:
Syrus Akbary 2015-11-16 19:48:51 -08:00
parent d2ca8a96a9
commit 24cd190f95
5 changed files with 49 additions and 6 deletions

View File

@ -8,7 +8,7 @@ Graphene is a Python library for building GraphQL schemas/types fast and easily.
- **Django:** Automatic *Django model* mapping to Graphene Types. Check a fully working [Django](http://github.com/graphql-python/swapi-graphene) implementation - **Django:** Automatic *Django model* mapping to Graphene Types. Check a fully working [Django](http://github.com/graphql-python/swapi-graphene) implementation
*But, what is supported in this Python version?* **Everything**: Interfaces, ObjectTypes, Mutations and Relay (Nodes, Connections and Mutations). *What is supported in this Python version?* **Everything**: Interfaces, ObjectTypes, Mutations, Scalars, Unions and Relay (Nodes, Connections and Mutations).
## Installation ## Installation

View File

@ -12,9 +12,9 @@ easily.
`Django <http://github.com/graphql-python/swapi-graphene>`__ `Django <http://github.com/graphql-python/swapi-graphene>`__
implementation implementation
*But, what is supported in this Python version?* **Everything**: *What is supported in this Python version?* **Everything**: Interfaces,
Interfaces, ObjectTypes, Mutations and Relay (Nodes, Connections and ObjectTypes, Mutations, Scalars, Unions and Relay (Nodes, Connections
Mutations). and Mutations).
Installation Installation
------------ ------------

View File

@ -13,8 +13,10 @@ class Options(object):
self.local_fields = [] self.local_fields = []
self.is_interface = False self.is_interface = False
self.is_mutation = False self.is_mutation = False
self.is_union = False
self.interfaces = [] self.interfaces = []
self.parents = [] self.parents = []
self.types = []
self.valid_attrs = DEFAULT_NAMES self.valid_attrs = DEFAULT_NAMES
def contribute_to_class(self, cls, name): def contribute_to_class(self, cls, name):

View File

@ -7,7 +7,7 @@ import six
from graphene import signals from graphene import signals
from graphql.core.type import (GraphQLInputObjectType, GraphQLInterfaceType, from graphql.core.type import (GraphQLInputObjectType, GraphQLInterfaceType,
GraphQLObjectType) GraphQLObjectType, GraphQLUnionType)
from ..exceptions import SkipField from ..exceptions import SkipField
from ..options import Options from ..options import Options
@ -51,10 +51,17 @@ class ObjectTypeMeta(type):
new_class._meta.is_interface = new_class.is_interface(parents) new_class._meta.is_interface = new_class.is_interface(parents)
new_class._meta.is_mutation = new_class.is_mutation(parents) new_class._meta.is_mutation = new_class.is_mutation(parents)
union_types = [p for p in parents if issubclass(p, BaseObjectType)]
new_class._meta.is_union = len(union_types) > 1
new_class._meta.types = union_types
assert not ( assert not (
new_class._meta.is_interface and new_class._meta.is_mutation) new_class._meta.is_interface and new_class._meta.is_mutation)
assert not (
new_class._meta.is_interface and new_class._meta.is_union)
# Add all attributes to the class. # Add all attributes to the class.
for obj_name, obj in attrs.items(): for obj_name, obj in attrs.items():
new_class.add_to_class(obj_name, obj) new_class.add_to_class(obj_name, obj)
@ -66,6 +73,8 @@ class ObjectTypeMeta(type):
new_class.add_extra_fields() new_class.add_extra_fields()
new_fields = new_class._meta.local_fields new_fields = new_class._meta.local_fields
assert not(new_class._meta.is_union and new_fields), 'An union cannot have extra fields'
field_names = {f.name: f for f in new_fields} field_names = {f.name: f for f in new_fields}
for base in parents: for base in parents:
@ -129,6 +138,8 @@ class BaseObjectType(BaseType):
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs):
if cls._meta.is_interface: if cls._meta.is_interface:
raise Exception("An interface cannot be initialized") raise Exception("An interface cannot be initialized")
if cls._meta.is_union:
raise Exception("An union cannot be initialized")
return super(BaseObjectType, cls).__new__(cls) return super(BaseObjectType, cls).__new__(cls)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -182,6 +193,12 @@ class BaseObjectType(BaseType):
resolve_type=partial(cls.resolve_type, schema), resolve_type=partial(cls.resolve_type, schema),
fields=partial(cls.get_fields, schema) fields=partial(cls.get_fields, schema)
) )
elif cls._meta.is_union:
return GraphQLUnionType(
cls._meta.type_name,
types=cls._meta.types,
description=cls._meta.description,
)
return GraphQLObjectType( return GraphQLObjectType(
cls._meta.type_name, cls._meta.type_name,
description=cls._meta.description, description=cls._meta.description,

View File

@ -4,7 +4,8 @@ from graphene.core.schema import Schema
from graphene.core.types import Int, Interface, String from graphene.core.types import Int, Interface, String
from graphql.core.execution.middlewares.utils import (resolver_has_tag, from graphql.core.execution.middlewares.utils import (resolver_has_tag,
tag_resolver) tag_resolver)
from graphql.core.type import GraphQLInterfaceType, GraphQLObjectType from graphql.core.type import (GraphQLInterfaceType, GraphQLObjectType,
GraphQLUnionType)
class Character(Interface): class Character(Interface):
@ -34,6 +35,16 @@ class Human(Character):
def write_prop(self, value): def write_prop(self, value):
self._write_prop = value self._write_prop = value
class Droid(Character):
'''Droid description'''
pass
class CharacterType(Droid, Human):
'''Union Type'''
pass
schema = Schema() schema = Schema()
@ -52,6 +63,19 @@ def test_interface_cannot_initialize():
assert 'An interface cannot be initialized' == str(excinfo.value) assert 'An interface cannot be initialized' == str(excinfo.value)
def test_union():
object_type = schema.T(CharacterType)
assert CharacterType._meta.is_union is True
assert isinstance(object_type, GraphQLUnionType)
assert object_type.description == 'Union Type'
def test_union_cannot_initialize():
with raises(Exception) as excinfo:
CharacterType()
assert 'An union cannot be initialized' == str(excinfo.value)
def test_interface_resolve_type(): def test_interface_resolve_type():
resolve_type = Character.resolve_type(schema, Human(object())) resolve_type = Character.resolve_type(schema, Human(object()))
assert isinstance(resolve_type, GraphQLObjectType) assert isinstance(resolve_type, GraphQLObjectType)