7

I want to create a UnionType(graphene.Union) of two existing types (FirstType and SecondType) and be able to resolve the query of this union type.


Schema

    class FirstType(DjangoObjectType):
        class Meta:
            model = FirstModel

    class SecondType(DjangoObjectType):
        class Meta:
            model = SecondModel

    class UnionType(graphene.Union):
        class Meta:
            types = (FirstType, SecondType)

So with this schema I want to query all objects from FirstType and SecondType with pk in some list [pks]

    query {
        all_items(pks: [1,2,5,7]){
          ... on FirstType{
             pk,
             color, 
          }

          ... on SecondType{       
             pk,        
             size,
          }
        }
     }

PKs from FirstType are normally not in the SecondType.

I tried like one below

    def resolve_items(root, info, ids):
        queryset1 = FirstModel.objects.filter(id__in=pks)
        queryset2 = SecondModel.objects.filter(id__in=pks)
        return queryset1 | queryset2

but it gives an error: 'Cannot combine queries on two different base models.'

I expect the following response from query:

    { 'data':
        {'all_items':[
           {'pk': 1,
            'color': blue
           },
           {'pk': 2,
            'size': 50.0
           },
           ...
          ]}
     }

So how the resolver should look like?

liyapo
  • 151
  • 1
  • 10

2 Answers2

7

The graphene Documentation on union types is very sparse. Here is a working example of how to do it correctly:

from graphene import ObjectType, Field, List, String, Int, Union

mock_data = {
    "episode": 3,
    "characters": [
        {
            "type": "Droid",
            "name": "R2-D2",
            "primaryFunction": "Astromech"
        },
        {
            "type": "Human",
            "name": "Luke Skywalker",
            "homePlanet": "Tatooine"
        },
        {
            "type": "Starship",
            "name": "Millennium Falcon",
            "length": 35
        }
    ]
}


class Human(ObjectType):
    name = String()
    homePlanet = String()


class Droid(ObjectType):
    name = String()
    primaryFunction = String()


class Starship(ObjectType):
    name = String()
    length = Int()


class Character(Union):
   class Meta:
       types = (Human, Droid, Starship)

   @classmethod
   def resolve_type(cls, instance, info):
      if instance["type"] == "Human":
         return Human
     if instance["type"] == "Droid":
         return Droid
      if instance["type"] == "Starship":
         return Starship


class RootQuery(ObjectType):
    result = Field(SearchResult)

    def resolve_result(_, info):
        return mock_data

Then, for a query like

  query Humans {
    result {
      episode
      characters {
        ... on Droid {
          name
        }
        ... on Starship {
          name
        }
        ... on Human {
          name
        }
      }
    }
  }

it returns the correct result.

jmandt
  • 386
  • 5
  • 13
5

Okay, so I was too concentrated on merging query sets and I didn't notice that I can simply return a list.

So here is solution which gives me the response I was looking for:

def resolve_items(root, info, ids):
    items = []
    queryset1 = FirstModel.objects.filter(id__in=pks)
    items.extend(queryset1)
    queryset2 = SecondModel.objects.filter(id__in=pks)
    items.extend(queryset2)
    return items
liyapo
  • 151
  • 1
  • 10
  • 2
    Just wanna say your solution worked for me! I simplified it a little by unpacking the lists directly in the return: `return [*queryset1, *queryset2]` – Samuel Gluss Feb 13 '20 at 01:24
  • this works for now but wiould be glad if I found a way.. amy polymorphic models has like 100 models and this would be so tiresome. if you anyone found a way then please update us – kim May 19 '21 at 10:05