Django Graphql

 

GraphQL is an open-source data query and manipulation language for APIs, and a runtime for fulfilling queries with existing data.It was developed internally by Facebook in 2012 before being publicly released in 2015. It allows clients to define the structure of the data required, and the same structure of the data is returned from the server, therefore preventing unnecessary data from being.

  1. Vue Django Admin
  2. Graphene Django Mutation
  3. Django Graphql Subscriptions
Graphql example

Over the past fifteen or so years, Django has become an integral part of a number of massive-scale internet products, such as Instagram, Pinterest and Spotify. As an Instagram engineer noted in September 2019, “Instagram Server is a Python monolith with several million lines of code and a few thousand Django endpoints.”. The advantage of being a full stack web developer is: You can master all the techniques involved in a development project; You can make a prototype very rapidly.

Graphene Django has a number of additional features that are designed to makeworking with Django easy. Our primary focus in this tutorial is to give a goodunderstanding of how to connect models from Django ORM to Graphene object types.

Set up the Django project¶

We will set up the project, create the following:

  • A Django project called cookbook
  • An app within cookbook called ingredients

Now sync your database for the first time:

Let’s create a few simple models...

Defining our models¶

Let’s get started with these models:

Add ingredients as INSTALLED_APPS:

Don’t forget to create & run migrations:

Load some test data¶

Now is a good time to load up some test data. The easiest option will beto download theingredients.jsonfixture and place it incookbook/ingredients/fixtures/ingredients.json. You can then run thefollowing:

Alternatively you can use the Django admin interface to create some datayourself. You’ll need to run the development server (see below), andcreate a login for yourself too (pythonmanage.pycreatesuperuser).

Register models with admin panel:

Hello GraphQL - Schema and Object Types¶

In order to make queries to our Django project, we are going to need few things:

  • Schema with defined object types
  • A view, taking queries as input and returning the result

GraphQL presents your objects to the world as a graph structure ratherthan a more hierarchical structure to which you may be accustomed. Inorder to create this representation, Graphene needs to know about eachtype of object which will appear in the graph.

This graph also has a root type through which all access begins. Thisis the Query class below.

To create GraphQL types for each of our Django models, we are going to subclass the DjangoObjectType class which will automatically define GraphQL fields that correspond to the fields on the Django models.

After we’ve done that, we will list those types as fields in the Query class.

Create cookbook/schema.py and type the following:

You can think of this as being something like your top-level urls.pyfile.

Testing everything so far¶

We are going to do some configuration work, in order to have a working Django where we can test queries, before we move on, updating our schema.

Update settings¶

Next, install your app and GraphiQL in your Django project. GraphiQL isa web-based integrated development environment to assist in the writingand executing of GraphQL queries. It will provide us with a simple andeasy way of testing our cookbook project.

Add graphene_django to INSTALLED_APPS in cookbook/settings.py:

And then add the SCHEMA to the GRAPHENE config in cookbook/settings.py:

Alternatively, we can specify the schema to be used in the urls definition,as explained below.

Creating GraphQL and GraphiQL views¶

Unlike a RESTful API, there is only a single URL from which GraphQL isaccessed. Requests to this URL are handled by Graphene’s GraphQLViewview.

This view will serve as GraphQL endpoint. As we want to have theaforementioned GraphiQL we specify that on the parameters with graphiql=True.

If we didn’t specify the target schema in the Django settings fileas explained above, we can do so here using:

Testing our GraphQL schema¶

We’re now ready to test the API we’ve built. Let’s fire up the serverfrom the command line.

Go to localhost:8000/graphql andtype your first query!

If you are using the provided fixtures, you will see the following response:

Congratulations, you have created a working GraphQL server 🥳!

Note: Graphene automatically camelcases all field names for better compatibility with JavaScript clients.

Getting relations¶

Graphql

Using the current schema we can query for relations too. This is where GraphQL becomes really powerful!

For example, we may want to get a specific categories and list all ingredients that are in that category.

We can do that with the following query:

This will give you (in case you are using the fixtures) the following result:

We can also list all ingredients and get information for the category they are in:

Summary¶

As you can see, GraphQL is very powerful and integrating Django models allows you to get started with a working server quickly.

If you want to put things like django-filter and automatic pagination in action, you should continue with the Relay tutorial.

A good idea is to check the Graphenedocumentation so that you are familiar with it as well.

I recently stumbled my way though integrating Django with Ariadne and am going to share the lessons learned here. We'll cover the following topics:

  • Setting up Django with Ariadne
  • Hooking up resolvers to our Django models
  • Modularizing our schema and resolvers
  • Writing tests
  • Dates, and Datetimes
  • Authentication
  • Accessing the User object

Setting up Django with Ariadne

To start we need to install Django and Ariadne and create an empty Django project:

To get the GraphQL server working, there's only two things we need to do. First we need to modify the INSTALLED_APPS in our settings file.

Then we need to modify our urls file to include Ariadne's GraphQLView with a placeholder schema:

Now if you run ./manage.py runserver and paste localhost:8000/graphql into the browser, you'll see the graphql playground:

Hooking up resolvers to our Django models

Let's make our example more realistic by creating resolvers that actually interact with a Django model. To do this we're going to need to create a Django app and add a model in the app's models.py:

Vue Django Admin

We'll start with two GraphQL operations: a query books and a mutation createBook. Let's modify our urls file with the new schema and resolvers:

Now if we visit the graphql playground again, we are able to create and list books:

Modularizing our schema and resolvers

Putting everything in our top-level urls file works fine for now, but this isn't going to scale. It would be better to move our schema into .graphql files that live in the Django apps they relate to. Secondly, we want to move our GraphQL config out of the top-level urls.py file and into a dedicated graphql_config.py. Let's start by moving our schema and resolvers related to book into the books app:


Our Query and Mutation types will live in a top-level schema file:

And our GraphQL config will be moved out of the 'urls.py' file into a dedicated 'graphql_config.py'.

Graphene Django Mutation

Now our urls file can be simplified to look like this:

Writing tests

We can test our GraphQL operations the same way we'd test any other Django view. GraphQL operations are sent as POST requests to the /graphql endpoint. The query/mutation is passed as a string in the JSON payload. As an example, here are tests for our createBook mutation and our books query:

Graphql

Converting between camel case and snake case

The JavaScript convention is to use camel case, while in python we use snake case. Ariadne provides a way to automatically convert between these two naming conventions. To make this work we have to tell Ariadne to use the snake_case_fallback_resolvers in our graphql config. This default resolver will map our resolver return values from snake case to camel case:

We'd also like to automatically convert our query/mutation arguments from camel case to snake case (i.e. the keyword arguments that get passed to our resolvers). For this Ariadne provides a decorator convert_kwargs_to_snake_case that we can wrap our resolvers with:

Let's demonstrate by adding a published_at field to our model and resolvers.


Django

In our GraphQL schemas, we're going to use the camel case version publishedAt:


Dates and Datetimes

Ariadne provides scalar mappings between ISO formatted strings and python datetime/date objects. This allows us to avoid manually converting between strings and datetime objects, and makes sure that they are converted to a string format readable by common javascript libraries like moment.js. First, we need to add the DateTime and Date scalars to our schema:


Now, we need to tell Ariadne how to convert our scalars to and from python objects by including date_scalar and datetime_scalar in our schema:

Authorization

If you are using GraphQL as the backend server for a web app like me, then you probably only want users who are logged in to be able to use the /graphql endpoint. The easiest way to do this is to wrap the GraphQL view with Django's @login_required decorator.

The login_required decorator will attempt redirect the use to the login page with a 302 if they aren't authenticated. Apollo client won't follow the redirect by default, so this is something you'll have to configure client side.

Accessing the User object

Ariadne provides access to the user object through the second positional argument, the 'info' object, in each resolver:

We can modify our list_books resolver to only return books associated with the authenticated user like this:

Django Graphql Subscriptions

In a normal HTTP request, Django's AuthenticationMiddleware puts the user on the request object. Since we're using graphql over HTTP, each graphql operation is carried in an HTTP request that passes through all the same middleware. That's how the user object gets populated.

Closing remarks

I hope this was helpful :)