Add documentation to help specifying types for Unions

Also add an additional test case, update existing ones, to cover
`resolve_type` and `Meta.possible_types` more
This commit is contained in:
Ramon Wenger 2022-06-15 16:03:58 +02:00
parent 9c3e4bb7da
commit 2c92618648
3 changed files with 102 additions and 10 deletions

View File

@ -61,3 +61,63 @@ The above types have the following representation in a schema:
union SearchResult = Human | Droid | Starship union SearchResult = Human | Droid | Starship
Resolving Types
~~~~~~~~~~~~~~~
When defining Unions, as with Interfaces, we need to tell the schema how to resolve the type of a returned object.
This can be achieved by:
- defining an `is_type_of`-method on each `ObjectType`
- defining the attribute `possible_types` on the Meta class
- defining a `resolve_type` on the Union
Examples:
^^^^^^^^^
An example with `is_type_of` and `Meta.possible_types`:
.. code:: python
class one_object:
one = 'one'
class two_object:
two = 'two'
class One(ObjectType):
one = String()
@classmethod
def is_type_of(cls, root, info):
return isinstance(root, one_object)
class Two(ObjectType):
class Meta:
possible_types = (two_object,)
two = String()
class MyUnion(Union):
class Meta:
types = (One, Two)
An example with `resolve_type`:
.. code:: python
class One(ObjectType):
one = String()
class Two(ObjectType):
two = String()
class MyUnion(Union):
class Meta:
types = (One, Two)
@classmethod
def resolve_type(cls, instance, info)
if hasattr(instance, 'one'):
return One
else:
return Two

View File

@ -65,12 +65,10 @@ def test_query_union():
return isinstance(root, one_object) return isinstance(root, one_object)
class Two(ObjectType): class Two(ObjectType):
class Meta:
possible_types = (two_object,)
two = String() two = String()
@classmethod
def is_type_of(cls, root, info):
return isinstance(root, two_object)
class MyUnion(Union): class MyUnion(Union):
class Meta: class Meta:
types = (One, Two) types = (One, Two)
@ -111,13 +109,10 @@ def test_query_interface():
class Two(ObjectType): class Two(ObjectType):
class Meta: class Meta:
interfaces = (MyInterface,) interfaces = (MyInterface,)
possible_types = (two_object,)
two = String() two = String()
@classmethod
def is_type_of(cls, root, info):
return isinstance(root, two_object)
class Query(ObjectType): class Query(ObjectType):
interfaces = List(MyInterface) interfaces = List(MyInterface)

View File

@ -4,14 +4,16 @@ from ..field import Field
from ..objecttype import ObjectType from ..objecttype import ObjectType
from ..union import Union from ..union import Union
from ..unmountedtype import UnmountedType from ..unmountedtype import UnmountedType
from ..schema import Schema
from ..scalars import String
class MyObjectType1(ObjectType): class MyObjectType1(ObjectType):
pass name = Field(String)
class MyObjectType2(ObjectType): class MyObjectType2(ObjectType):
pass not_name = Field(String)
def test_generate_union(): def test_generate_union():
@ -56,3 +58,38 @@ def test_union_can_be_mounted():
my_union_field = my_union_instance.mount_as(Field) my_union_field = my_union_instance.mount_as(Field)
assert isinstance(my_union_field, Field) assert isinstance(my_union_field, Field)
assert my_union_field.type == MyUnion assert my_union_field.type == MyUnion
def test_resolve_type_custom():
class MyUnion(Union):
class Meta:
types = (MyObjectType1, MyObjectType2)
@classmethod
def resolve_type(cls, instance, info):
if 'name' in instance:
return MyObjectType1
else:
return MyObjectType2
class Query(ObjectType):
test = Field(MyUnion)
def resolve_test(_, info):
return {'name': 'Type 1'}
schema = Schema(query=Query)
result = schema.execute(
"""
query {
test {
__typename
...on MyObjectType1 {
name
}
}
}
"""
)
assert not result.errors
assert result.data == {"test": {"__typename": "MyObjectType1", "name": "Type 1"}}