I’m trying to implement GraphQL mutation for a "complex" object. Let’s say we have a Contact
with three fields: firstName
, lastName
and address
, which is object with one field street
:
Here is my python scheme implementation:
class Address(graphene.ObjectType):
street = graphene.String(description='Street Name')
class AddressInput(graphene.InputObjectType):
street = graphene.String(description='Street Name', required=True)
class Contact(graphene.ObjectType):
first_name = graphene.String(description='First Name')
last_name = graphene.String(description='Last Name')
address = graphene.Field(Address, description='Contact address')
class ContactInput(graphene.InputObjectType):
first_name = graphene.String(description='First Name', required=True)
last_name = graphene.String(description='Last Name', required=True)
address = AddressInput(description='Contact address', required=True)
class CreateContact(graphene.Mutation):
class Input:
contact = ContactInput()
contact = graphene.Field(Contact, description='Created Contact object')
@staticmethod
def mutate(instance, args, context, info):
contact = Contact(**args['contact'])
return CreateContact(contact=contact)
and when I run this query:
mutation CreateContact($contact: ContactInput!) {
createContact(contact: $contact) {
contact {
firstName
address {
street
}
}
}
}
with the following variables:
{
"contact": {
"address": {
"street": "Bond Blvd"
},
"firstName": "John",
"lastName": "Doe"
}
}
I get the following result:
{
"createContact": {
"contact": {
"address": {
"street": null
},
"firstName": "John"
}
}
}
As you can see, street
field is null
in results.
I can get what I need if I change mutate
method to be like:
@staticmethod
def mutate(instance, args, context, info):
args['contact']['address'] = Address(**args['contact']['address'])
contact = Contact(**args['contact'])
return CreateContact(contact=contact)
But I’m not sure this is a proper way.
So please advise a correct way to initiate nested structures.
1 Answer
Which version of graphene are you using? It looks like the default resovler being used for the Adress ObjectType does not resolve dictionaries.
In Graphene by default https://docs.graphene-python.org/en/latest/types/objecttypes/#defaultresolver:
the resolver will look for a dictionary key matching the field name. Otherwise, the resolver will get the attribute from the parent value object matching the field name
You can try configuring the Address class with an specific resolver:
from graphene.types.resolver import dict_or_attr_resolver, dict_resolver
class Address(graphene.ObjectType):
street = graphene.String(description="Street Name")
class Meta:
default_resolver = dict_or_attr_resolver
Also, for me with the current last version of graphene (3.3) the following is working just fine:
class Address(graphene.ObjectType):
street = graphene.String(description="Street Name")
class AddressInput(graphene.InputObjectType):
street = graphene.String(description="Street Name", required=True)
class Contact(graphene.ObjectType):
first_name = graphene.String(description="First Name")
last_name = graphene.String(description="Last Name")
address = graphene.Field(Address, description="Contact address")
class ContactInput(graphene.InputObjectType):
first_name = graphene.String(description="First Name", required=True)
last_name = graphene.String(description="Last Name", required=True)
address = AddressInput(description="Contact address", required=True)
class CreateContact(graphene.Mutation):
class Input:
contact = ContactInput()
contact = graphene.Field(Contact, description="Created Contact object")
def mutate(root, info, **args):
contact = Contact(**args["contact"])
return CreateContact(contact=contact)
def test_resolver():
query = """
mutation CreateContact($contact: ContactInput!) {
createContact(contact: $contact) {
contact {
firstName
address {
street
}
}
}
}
"""
client = Client(schema)
response = client.execute(
query,
variables={
"contact": {
"address": {"street": "Bond Blvd"},
"firstName": "John",
"lastName": "Doe",
}
},
)
assert response == {
"data": {
"createContact": {
"contact": {
"address": {
"street": "Bond Blvd",
},
"firstName": "John",
}
}
}
}