Mutation and Query In GraphQL Using Python/Django PART 2.
Previously, we looked at the code implementation of GraphQL’s query method from the server side and also how to use the Interactive Graphql Console to query(get) the data from the database via the client. In this part we shall be considering GraphQL Mutation!
GraphQL Mutation
Mutation in graphql is the equivalent of the PUT, DELETE, UPDATE, POST and PATCH Http verbs/methods for RESTFUL services. we can also say mutation is what graphql uses to modify server-side data as well.
Django-Server-Implementation(GraphQL)
As with the book model from the previous part where we created a query, let’s also create a mutation for it as well…
Back to the schema.py file let’s introduce a Create and an Update Mutation for the book model on our server
# schema.py
...
import graphene
from graphene import InputObjectType
class BookCreateInput(InputObjectType):
"""
Class defined to accept input data
from the interactive graphql console.
"""
title = graphene.String(required=False)
isbn = graphene.String(required=False)
category = graphene.String(required=False)
Data sent in from the client would usually pass through this class definition as input data, here we define the fields that we are expecting from the client for any book mutation, remember the book model has this same fields.
With the input class defined,the next thing to do is to define the Mutation for the book creation and update process.
schema.py
class CreateBook(relay.ClientIDMutation):
class Input:
# BookCreateInput class used as argument here.
book = graphene.Argument(BookCreateInput)
new_book = graphene.Field(BookNode)
@classmethod
def mutate_and_get_payload(cls, args, context, info):
book_data = args.get('book') # get the book input from the args
book = Book() # get an instance of the book model here
new_book = update_create_instance(book, book_data) # use custom function to create book
return cls(new_book=new_book) # newly created book instance returned.
class UpdateBook(relay.ClientIDMutation):
class Input:
book = graphene.Argument(BookCreateInput) # get the book input from the args
id = graphene.String(required=True) # get the book id
errors = graphene.List(graphene.String)
updated_book = graphene.Field(BookNode)
@classmethod
def mutate_and_get_payload(cls, args, context, info):
try:
book_instance = get_object(Book, args['id']) # get book by id
if book_instance:
# modify and update book model instance
book_data = args.get('book')
updated_book = update_create_instance(book_instance, book_data)
return cls(updated_book=updated_book)
except ValidationError as e:
# return an error if something wrong happens
return cls(updated_book=None, errors=get_errors(e))
# helper function
"""Script defined to create helper functions for graphql schema."""
from graphql_relay.node.node import from_global_id
def get_object(object_name, relayId, otherwise=None):
try:
return object_name.objects.get(pk=from_global_id(relayId)[1])
except:
return otherwise
def update_create_instance(instance, args, exception=['id']):
if instance:
[setattr(instance, key, value) for key, value in args.items() if key not in exception]you
# caution if you literally cloned this project, then be sure to have
# elasticsearch running as every saved instance must go through
# elasticsearch according to the way this project is configured.
instance.save()
return instance
def get_errors(e):
# transform django errors to redux errors
# django: {"key1": [value1], {"key2": [value2]}}
# redux: ["key1", "value1", "key2", "value2"]
fields = e.message_dict.keys()
messages = ['; '.join(m) for m in e.message_dict.values()]
errors = [i for pair in zip(fields, messages) for i in pair]
return errors
With the Mutation classes defined, the final step would be to add the Mutation class to the schema file.
class Mutation(ObjectType):
create_book = CreateBook.Field()
update_book = UpdateBook.Field()
schema = graphene.Schema(
query=Query,
mutation=Mutation,
)
TEST YOUR NEW FEATURE…
Time for some fun!!
Well, we have pretty much come to the end of the server side implementation now it’s time to test out our new mutation functionality…
Run the server as usual and navigate to localhost:8000/graphql
and let’s make a mutation to create a new book on the server
Create New Book
The Mutation
mutation CreateNewBook($input: CreateBookInput!){
createBook(input: $input) {
newBook {
id
title
isbn
}
clientMutationId
}
}
and Query Variable
{"input": { "book":
{
"title": "The Famous Five",
"isbn": "34vr34jsad",
"category": "Fantasy"
}
}}
Tells the server to create a new book with the data passed via the query variable which is graphql’s way of dynamically setting data.
Naming a mutation “mutation CreateNewBook” is a way of identifying which mutation process is been called and the definition “($input: CreateBookInput!)” pair is used to tell the mutation that we are going to pass in an “input” that is of type CreateBookInput! (NOT NULLABLE).
The next line createBook is used to create the book where we pass in the variable “$input” via the “query variable” defined.
The newBook object tells graphql what we want to be returned after the book instance is created.
To update an existing book on the server
The Mutation
mutation UpdateBookById($input: UpdateBookInput!) {
updateBook(input: $input) {
updatedBook {
title
}
}
}
and Query Variable
{"input": { "id": "Qm9va05vZGU6Tm9uZQ==", "book": {
"title": "The Famous 5",
"isbn": "599223"
}}}
Allows us to modify an already existing data.
Conclusion
This is where we put a hard stop to this tutorial, we have looked at query and mutation with graphql as well as the server side implementation of graphql using Python/Django. We certainly did not look at how to delete data from the server side and I suppose you can experiment with that to enforce the idea.
Thanks for reading and feel free to like and share this post.