mirror of
https://github.com/graphql-python/graphene.git
synced 2025-02-02 12:44:15 +03:00
Added support for UnionTypes
This commit is contained in:
parent
d2ca8a96a9
commit
24cd190f95
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
------------
|
------------
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user