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?
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
}
}
}
}