{"id":115,"date":"2016-11-06T07:02:02","date_gmt":"2016-11-05T23:02:02","guid":{"rendered":"http:\/\/codestrian.com\/?p=115"},"modified":"2018-08-14T10:34:06","modified_gmt":"2018-08-14T02:34:06","slug":"how-to-get-started-with-graphql-on-django","status":"publish","type":"post","link":"https:\/\/codestrian.com\/index.php\/2016\/11\/06\/how-to-get-started-with-graphql-on-django\/","title":{"rendered":"How to get started with GraphQL on Django"},"content":{"rendered":"<p>ok so I suppose that you are here because you have not been able to find enough resources out there regarding the usage of GraphQL with Django. Congratulation, you have come to the right place. After slogging through the internet to find all the information that is needed, I have decided to put up an article regarding some basic usage of GraphQL, mainly CRUD operations, with Django.<\/p>\n<p>So lets get started from the scratch with a new django project.<br \/>\nFirst thing, install django and graphene for django.<br \/>\ngraphene_django is a python package that allows django to act as a GraphQL Server. And django-filter is needed for the example to handles filters in query, you may not need this in your own project.<\/p>\n<pre><code>pip install django\npip install graphene_django\npip install django-filter <\/code><\/pre>\n<p>If you have virtualenv\/virtualenvwrapper installed, feel free to use it before running this.<\/p>\n<p>Next lets create a project folder for django<\/p>\n<pre><code>mkdir GraphQLExample\ncd GraphQLExample<\/code><\/pre>\n<p>Create the django project and django app.<\/p>\n<pre><code>django-admin.py startproject catalog\ncd catalog\ndjango-admin.py startapp books\npython3 manage.py migrate<\/code><\/pre>\n<p>Please check and make sure that your project folder structure looks like this:<\/p>\n<pre>GraphQLExample\n|_catalog\n  |_catalog\n  |_books\n  |_manage.py  \n<\/pre>\n<p>next, we will create some django models in <strong>books\/models.py<\/strong><\/p>\n<pre><code>from django.db import models<\/code>\n\n# Create your models here.\nclass Category(models.Model):\nname = models.CharField(max_length=100)\n\ndef __str__(self):\nreturn self.name\n\nclass Book(models.Model):\nname = models.CharField(max_length=100)\nauthor = models.CharField(max_length=100)\ncategory = models.ForeignKey(Category, related_name='books')\n\ndef __str__(self):\nreturn self.name\n<\/pre>\n<p>The following part is where we will set up our GraphQL backend. create a <strong>schema.py<\/strong> in <strong>books<\/strong><br \/>\n<strong>schema.py<\/strong> is synonymous to urls.py in a django project. In this file, we will define how our queries will be handled by the GraphQL backend. we will start with simple queries like list and retrieve. Later in the article, I will touch on how to use <strong>mutation<\/strong> to do create\/update\/delete operations.<\/p>\n<pre><code>from graphene import AbstractType\nfrom graphene import Field\nfrom graphene import Node\nfrom graphene import ClientIDMutation\nfrom graphene import String\nfrom graphene import Float\n\nfrom graphene_django.filter import DjangoFilterConnectionField\nfrom graphene_django.types import DjangoObjectType\n\nfrom .models import Category\nfrom .models import Book\n\nclass CategoryNode(DjangoObjectType):\n    class Meta:\n        model = Category\n        interfaces = (Node, )\n        filter_fields = ['name', 'books']\n        filter_order_by = ('name')\n\n\nclass BookNode(DjangoObjectType):\n    class Meta:\n        model = Book\n        interfaces = (Node, )\n        filter_fields = {\n            'name': ['exact', 'icontains', 'istartswith'],\n            'author': ['exact', 'icontains'],\n            'category': ['exact'],\n            'category__name': ['exact'],\n        }\n        filter_order_by = ('name', 'category__name')\n\nclass CategoryQuery(AbstractType):\n     category = DjangoFilterConnectionField(CategoryNode)\n \nclass BookQuery(AbstractType):\n     book = Node.Field(BookNode)\n     all_books = DjangoFilterConnectionField(BookNode)\n<\/code><\/pre>\n<p>Let me briefly explain what is actually happening here. In the schema.py found in the app, we have defined 2 Node classes, BookNode and CategoryNode. If you are familiar with django RESTful framework, they are pretty similar to serializer. In the Meta class of BookNode and CategoryNode, you can see that several options has been indicated to define the behaviour that is supported when queried. There are additional options like <strong>exclude_field<\/strong> and <strong>only_field<\/strong>. You may dig through the <a href=\"https:\/\/github.com\/graphql-python\/graphene-django\/blob\/d73f4aa23581d7604cd92a80e8890ad490170094\/graphene_django\/types.py\">source code<\/a> of graphene for more information.<\/p>\n<p>After defining the Node Classes, we will need to consolidate them into a Query class that is of type AbstractType. The reason why this is AbstractType is to allow inheritance later by the Root Query class. the snake_case in the BookQuery and CategoryQuery will be transformed into camelCase. So in your query, you will refer to allBooks instead of all_books.<\/p>\n<p>Next, we will create a root schema in the catalog folder. <strong>catalog\/schema.py<\/strong><\/p>\n<pre><code>import graphene\nfrom books.schema import BookQuery\nfrom books.schema import CategoryQuery\n\nclass RootQuery(BookQuery\n        , CategoryQuery\n        , graphene.ObjectType):\n    pass\n\nschema = graphene.Schema(query=RootQuery)<\/code><\/pre>\n<p>Next we will need to enable GraphQL in our project. This is achieved by changing the django project settings.<br \/>\nOpen catalog\/settings.py and add the following to the file.<\/p>\n<pre><code>\nINSTALLED_APPS = (\n   ...\n   'books',\n   'graphene_django',\n)\n\nGRAPHENE = {\n    'SCHEMA' : 'catalog.schema.schema', #points to the schema variable in schema.py\n    'SCHEMA_INDENT': 2, #defines the indentation space in the output\n}<\/code><\/pre>\n<p>Now we need to add an url in the <strong>catalog\/urls.py<\/strong> so that our queries can be received by django backend.<\/p>\n<pre><code>from django.conf.urls import include, url\nfrom django.contrib import admin\nfrom graphene_django.views import GraphQLView\n\nurlpatterns = [\n    url(r'^admin\/', include(admin.site.urls)),\n    url(r'^api\/v1\/', GraphQLView.as_view(graphiql=True)),\n]<\/code><\/pre>\n<p>Time to do django migrations to initialize the database<\/p>\n<pre><code>\npython3 manage.py migrate\npython3 manage.py makemigrations books\npython3 manage.py migrate books\n<\/code><\/pre>\n<p>Run the server and open <a href=\"localhost:8000\/api\/v1\/\">localhost:8000\/api\/v1\/<\/a><\/p>\n<pre><code>python3 manage.py runserver<\/code><\/pre>\n<p>You will be able to see the interactive query window in your browser<br \/>\nIn the event that you are getting assertion error on \"order_by\", downgrade django-filter to 0.11.0.<\/p>\n<p>Try the follow query in the browser<\/p>\n<pre><code>{\n  allBooks{\n    edges{\n      node{\n        id\n        name\n        author\n      }\n    }\n  }\n}<\/code><\/pre>\n<p>You should be seeing the following in your browser<\/p>\n<pre><code>{\n  \"data\": {\n    \"allBooks\": {\n      \"edges\": []\n    }\n  }\n}<\/code><\/pre>\n<p>As you can see, the dataset is currently empty. Instead of loading a json file to populate the database like what other GraphQL examples did, I am gonna show you how to write the mutation to create a new record.<\/p>\n<p>Add the following code into your <strong>books\/schema.py<\/strong><\/p>\n<pre><code>class NewCategory(ClientIDMutation):\n    category = Field(CategoryNode)\n    class Input:\n        name = String()\n\n    @classmethod\n    def mutate_and_get_payload(cls, input, context, info):\n        temp = Category(\n             name = input.get('name') ,\n        )\n        temp.save()\n        return NewCategory(category=temp)\n\nclass NewBook(ClientIDMutation):\n    book = Field(BookNode)\n    class Input:\n        name = String()\n        author = String()\n        category = String()\n    \n    @classmethod\n    def mutate_and_get_payload(cls, input, context, info):\n        book = Book(\n            name = input.get('name'),\n            author = input.get('author'),\n            category = Category.objects.get(name=input.get('category'))            \n        )\n        book.save()\n        return NewBook(book=book)\n\nclass CategoryMutation(AbstractType):\n    new_category = NewCategory.Field()\n\nclass BookMutation(AbstractType):\n    new_Book = NewBook.Field()\n<\/code><\/pre>\n<p>Now we need to create a RootMutation in the Root schema. In the <strong>catalog\/schema.py<\/strong><br \/>\nChanges are highlighted in bold.<\/p>\n<pre><code>import graphene\nfrom books.schema import BookQuery\nfrom books.schema import CategoryQuery\n<strong>from books.schema import BookMutation\nfrom books.schema import CategoryMutation<\/strong>\n\nclass RootQuery(BookQuery\n        , CategoryQuery\n        , graphene.ObjectType):\n    pass\n\n<strong>class RootMutation(BookMutation\n        , CategoryMutation\n        , graphene.ObjectType):\n    pass<\/strong>\n\nschema = graphene.Schema(query=RootQuery<strong>, mutation=RootMutation<\/strong>)\n<\/code><\/pre>\n<p>As you can see that it is very similar to how Query is handled.<br \/>\nRun the following queries in the browser to add 2 categories and 2 books<\/p>\n<pre><code>mutation{\n  newCategory(input:{name:\"Fiction\"}){\n    category{\n      name\n    }\n  }\n}\nmutation{\n  newCategory(input:{name:\"Sci-fi\"}){\n    category{\n      name\n    }\n  }\n}\n\nmutation{\n  newBook(input:{name:\"Harry Potter\", author:\"JK Rowing\", category:\"Fiction\"}){\n    book{\n      name\n      author\n      category{\n        name\n      }\n    }\n  }\n}\nmutation{\n  newBook(input:{name:\"Starwars\", author:\"George Lucas\", category:\"Sci-fi\"}){\n    book{\n      name\n      author\n      category{\n        name\n      }\n    }\n  }\n}\n<\/code><\/pre>\n<p>Let's take a look at the data that has been inserted so far<\/p>\n<pre><code>\n{\n  allBooks{\n    edges{\n      node{\n        name\n        author\n        category{\n          name\n        }\n      }\n    }\n  }\n}\n<\/code><\/pre>\n<p>The output that you should be seeing in your browser:<\/p>\n<pre><code>{\n  \"data\": {\n    \"allBooks\": {\n      \"edges\": [\n        {\n          \"node\": {\n            \"name\": \"Harry Potter\",\n            \"author\": \"JK Rowing\",\n            \"category\": {\n              \"name\": \"Fiction\"\n            }\n          }\n        },\n        {\n          \"node\": {\n            \"name\": \"Starwars\",\n            \"author\": \"George Lucas\",\n            \"category\": {\n              \"name\": \"Sci-fi\"\n            }\n          }\n        }\n      ]\n    }\n  }\n}<\/code><\/pre>\n<p>Now that we have learnt how to create a new record in our database, lets try to update the record.<br \/>\nWe will now need to create a new mutation method that allow us to update the book information<br \/>\nAdd the following code into your <strong>book\/schema.py<\/strong><\/p>\n<pre><code>from graphql_relay.node.node import from_global_id\n\nclass UpdateBook(ClientIDMutation):\n    book = Field(BookNode)\n    class Input:\n        id = String()\n        name = String()\n        author = Float()\n    \n    @classmethod\n    def mutate_and_get_payload(cls, input, context, info):\n        book = Book.objects.get(pk=from_global_id(input.get('id'))[1])\n        book.name=input.get('name')\n        book.author=input.get('author')\n        book.save()\n        return UpdateBook(book=book)\n\nclass BookMutation(AbstractType):\n    new_Book = NewBook.Field()\n    update_book = UpdateBook.Field()<\/code><\/pre>\n<p>Unlike Create, you will need the id of the book in order to update it. However the id given by GraphQL is different from the id in django model. Fortunately, there is a way to convert the GraphQL id to Django model id by using <strong>from_global_id<\/strong>.<\/p>\n<p>Find the id of the book using the allBooks method.<\/p>\n<pre><code>\n{\n  allBooks{\n    edges{\n      node{\n        id\n        name\n        author\n      }\n    }\n  }\n}\n<\/code><\/pre>\n<p>The output in my browser shows:<\/p>\n<pre><code>{\n  \"data\": {\n    \"allBooks\": {\n      \"edges\": [\n        {\n          \"node\": {\n            \"id\": \"Qm9va05vZGU6MQ==\",\n            \"name\": \"Starwars\",\n            \"author\": \"George Lucas\"\n          }\n        },\n        {\n          \"node\": {\n            \"id\": \"Qm9va05vZGU6Mg==\",\n            \"name\": \"Starwars: new hope\",\n            \"author\": \"George Lucas\"\n          }\n        }\n      ]\n    }\n  }\n}<\/code><\/pre>\n<p>I am going to update the book with <strong>id = \"Qm9va05vZGU6Mg==\"<\/strong>. The one that you see might be different, so please change accordingly.<\/p>\n<pre><code>mutation{\n  updateBook(input:{id:\"Qm9va05vZGU6Mg==\", name:\"Starwars: new hope\", author:\"George Lucas\"}){\n    book{\n      name\n      author\n      category{\n        name\n      }\n    }\n  }\n}<\/code><\/pre>\n<p>To delete the object, is very similar to update and create. So I will leave that to you to explore.<br \/>\nI hope that after reading this you will be able to start using GraphQL in your django project. =)<\/p>\n<p>You may find all the code in my github repository <a href=\"https:\/\/github.com\/lebeier\/GraphGLExample\">here<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>ok so I suppose that you are here because you have not been able to find enough resources out there regarding the usage of GraphQL with Django. Congratulation, you have come to the right place. After slogging through the internet to find all the information that is needed, I have [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[7,18,6],"_links":{"self":[{"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/posts\/115"}],"collection":[{"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/comments?post=115"}],"version-history":[{"count":22,"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/posts\/115\/revisions"}],"predecessor-version":[{"id":296,"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/posts\/115\/revisions\/296"}],"wp:attachment":[{"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/media?parent=115"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/categories?post=115"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codestrian.com\/index.php\/wp-json\/wp\/v2\/tags?post=115"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}