From 2c92618648f68533b62ad50b2bb8d26ebccbb63e Mon Sep 17 00:00:00 2001 From: Ramon Wenger Date: Wed, 15 Jun 2022 16:03:58 +0200 Subject: [PATCH] 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 --- docs/types/unions.rst | 60 ++++++++++++++++++++++++++++++ graphene/types/tests/test_query.py | 11 ++---- graphene/types/tests/test_union.py | 41 +++++++++++++++++++- 3 files changed, 102 insertions(+), 10 deletions(-) diff --git a/docs/types/unions.rst b/docs/types/unions.rst index 16ac24e8..e17b63bc 100644 --- a/docs/types/unions.rst +++ b/docs/types/unions.rst @@ -61,3 +61,63 @@ The above types have the following representation in a schema: 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 diff --git a/graphene/types/tests/test_query.py b/graphene/types/tests/test_query.py index e117754f..a1fc4a2d 100644 --- a/graphene/types/tests/test_query.py +++ b/graphene/types/tests/test_query.py @@ -65,12 +65,10 @@ def test_query_union(): return isinstance(root, one_object) class Two(ObjectType): + class Meta: + possible_types = (two_object,) two = String() - @classmethod - def is_type_of(cls, root, info): - return isinstance(root, two_object) - class MyUnion(Union): class Meta: types = (One, Two) @@ -111,13 +109,10 @@ def test_query_interface(): class Two(ObjectType): class Meta: interfaces = (MyInterface,) + possible_types = (two_object,) two = String() - @classmethod - def is_type_of(cls, root, info): - return isinstance(root, two_object) - class Query(ObjectType): interfaces = List(MyInterface) diff --git a/graphene/types/tests/test_union.py b/graphene/types/tests/test_union.py index 4d642d6f..daa31c1b 100644 --- a/graphene/types/tests/test_union.py +++ b/graphene/types/tests/test_union.py @@ -4,14 +4,16 @@ from ..field import Field from ..objecttype import ObjectType from ..union import Union from ..unmountedtype import UnmountedType +from ..schema import Schema +from ..scalars import String class MyObjectType1(ObjectType): - pass + name = Field(String) class MyObjectType2(ObjectType): - pass + not_name = Field(String) def test_generate_union(): @@ -56,3 +58,38 @@ def test_union_can_be_mounted(): my_union_field = my_union_instance.mount_as(Field) assert isinstance(my_union_field, Field) 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"}}