I’m trying to have a unique interface for two concrete classes that are similar and inherit from a common abstract class.
My django model classes:
class Metadata(models.Model): name = models.CharField(max_length=255) sequence = models.PositiveSmallIntegerField() is_choices = False class Meta: abstract = True class MetadataScalar(Metadata): string_format = models.CharField(max_length=255, blank=True, null=True) class MetadataChoices(Metadata): is_choices = True choices = models.CharField(max_length=255, blank=True, null=True)
My graphene-django api:
class MetadataNode(DjangoObjectType): class Meta: interfaces = (Node,) connection_class = Connection model = Metadata fields = '__all__' class MetadataScalarNode(MetadataNode): class Meta: interfaces = (Node,) connection_class = Connection model = MetadataScalar fields = '__all__' class MetadataChoicesNode(MetadataNode): class Meta: interfaces = (Node,) connection_class = Connection model = MetadataChoices fields = '__all__' class CreateMetadata(ClientIDMutation): metadata = Field(MetadataNode) class Input: name = String(max_length=255, required=True) sequence = Int(required=True) string_format = String() choices = List(String) @classmethod def mutate_and_get_payload(cls, root, info, **input): if 'string_format' in input: metadata = MetadataScalar.objects.create( name=input.get('name'), sequence=input.get('sequence'), string_format=input.get('string_format') ) elif 'choices' in input: metadata = MetadataChoices.objects.create( name=input.get('name'), sequence=input.get('sequence'), choices=','.join(input.get('choices')) ) return CreateMetadata(metadata=metadata)
When querying the graphql mutation corresponding to CreateMetadata
, the metadata concrete class is successfully created. ;-)
The problem is that when the query asks for the created concrete Metadata
in the result (here either MetadataScalar
or MetadataChoices
), graphql cannot find a node for the concrete class and outputs the following error message:
Expected value of type "MetadataNode" but got: MetadataScalar.
For your information, here is one example query:
mutation { createMetadata (input: { stringFormat: "foo" sequence: 12 name: "bar" }) { metadata { name sequence } } }
How to make it work nicely, without having to specify two different result types (metadataScalar
and metadataChoices
variables) in the second part of the query?
Advertisement
Answer
You can use Union in order to be able to specify multiple different result classes.
In your case, that woud be:
class MetadataScalarNode(DjangoObjectType): class Meta: interfaces = (Node,) connection_class = Connection model = MetadataScalar fields = '__all__' class MetadataChoicesNode(DjangoObjectType): class Meta: interfaces = (Node,) connection_class = Connection model = MetadataChoices fields = '__all__' class MetadataNode(Union): class Meta: types = (MetadataScalarNode, MetadataChoicesNode)
The graphql query wil look like:
mutation { createMetadata (input: { stringFormat: "foo" sequence: 12 name: "bar" }) { metadata { __typename ... on MetadataScalarNode { name sequence stringFormat } ... on MetadataChoicesNode { name sequence choices } } } }