# Relay tutorial

Graphene has a number of additional features that are designed to make working with Django really simple.

Note: The code in this quickstart is pulled from the cookbook example app.

A good idea is to check the following things first:

- Graphene Relay documentation

- GraphQL Relay Specification

## Setup the Django project

We will setup the project, create the following:

- A Django project called `cookbook`

- An app within `cookbook` called `ingredients`

    # Create the project directory
    mkdir cookbook
    cd cookbook

    # Create a virtualenv to isolate our package dependencies locally
    virtualenv env
    source env/bin/activate  # On Windows use `env\Scripts\activate`

    # Install Django and Graphene with Django support
    pip install django
    pip install graphene_django

    # Set up a new project with a single application
    django-admin.py startproject cookbook .  # Note the trailing '.' character
    cd cookbook
    django-admin.py startapp ingredients

Now sync your database for the first time:

    python manage.py migrate

Let’s create a few simple models…

### Defining our models

Let’s get started with these models:

    # cookbook/ingredients/models.py
    from django.db import models

    class Category(models.Model):
        name = models.CharField(max_length=100)

        def __str__(self):
            return self.name

    class Ingredient(models.Model):
        name = models.CharField(max_length=100)
        notes = models.TextField()
        category = models.ForeignKey(Category, related_name='ingredients', on_delete=models.CASCADE)

        def __str__(self):
            return self.name

Don’t forget to create & run migrations:

    python manage.py makemigrations
    python manage.py migrate

### Load some test data

Now is a good time to load up some test data. The easiest option will be to download the ingredients.json fixture and place it in `cookbook/ingredients/fixtures/ingredients.json`. You can then run the following:

    $ python ./manage.py loaddata ingredients

    Installed 6 object(s) from 1 fixture(s)

Alternatively you can use the Django admin interface to create some data yourself. You’ll need to run the development server (see below), and create a login for yourself too (`./manage.py createsuperuser`).

## Schema

GraphQL presents your objects to the world as a graph structure rather than a more hierarchical structure to which you may be accustomed. In order to create this representation, Graphene needs to know about each type of object which will appear in the graph.

This graph also has a root type through which all access begins. This is the `Query` class below. In this example, we provide the ability to list all ingredients via `all_ingredients`, and the ability to obtain a specific ingredient via `ingredient`.

Create `cookbook/ingredients/schema.py` and type the following:

    # cookbook/ingredients/schema.py
    from graphene import relay, ObjectType
    from graphene_django import DjangoObjectType
    from graphene_django.filter import DjangoFilterConnectionField

    from ingredients.models import Category, Ingredient

    # Graphene will automatically map the Category model's fields onto the CategoryNode.
    # This is configured in the CategoryNode's Meta class (as you can see below)
    class CategoryNode(DjangoObjectType):
        class Meta:
            model = Category
            fields = '__all__'
            filter_fields = ['name', 'ingredients']
            interfaces = (relay.Node, )

    class IngredientNode(DjangoObjectType):
        class Meta:
            model = Ingredient
            fields = '__all__'
            # Allow for some more advanced filtering here
            filter_fields = {
                'name': ['exact', 'icontains', 'istartswith'],
                'notes': ['exact', 'icontains'],
                'category': ['exact'],
                'category__name': ['exact'],
            }
            interfaces = (relay.Node, )

    class Query(ObjectType):
        category = relay.Node.Field(CategoryNode)
        all_categories = DjangoFilterConnectionField(CategoryNode)

        ingredient = relay.Node.Field(IngredientNode)
        all_ingredients = DjangoFilterConnectionField(IngredientNode)

The filtering functionality is provided by django-filter. See the usage documentation for details on the format for `filter_fields`. While optional, this tutorial makes use of this functionality so you will need to install `django-filter` for this tutorial to work:

    pip install django-filter

Note that the above `Query` class is marked as ‘abstract’. This is because we will now create a project-level query which will combine all our app-level queries.

Create the parent project-level `cookbook/schema.py`:

    import graphene

    import ingredients.schema

    class Query(ingredients.schema.Query, graphene.ObjectType):
        # This class will inherit from multiple Queries
        # as we begin to add more apps to our project
        pass

    schema = graphene.Schema(query=Query)

You can think of this as being something like your top-level `urls.py` file (although it currently lacks any namespacing).

## Testing everything so far

### Update settings

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

Add `ingredients` and `graphene_django` to `INSTALLED_APPS` in `cookbook/settings.py`:

    INSTALLED_APPS = [
        ...
        # This will also make the `graphql_schema` management command available
        'graphene_django',

        # Install the ingredients app
        'ingredients',
    ]

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

    GRAPHENE = {
        'SCHEMA': 'cookbook.schema.schema'
    }

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 is accessed. Requests to this URL are handled by Graphene’s `GraphQLView` view.

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

    from django.conf.urls import url, include
    from django.contrib import admin

    from graphene_django.views import GraphQLView

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
    ]

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

    from django.conf.urls import url, include
    from django.contrib import admin

    from graphene_django.views import GraphQLView

    from cookbook.schema import schema

    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^graphql$', GraphQLView.as_view(graphiql=True, schema=schema)),
    ]

### Testing our GraphQL schema

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

    $ python ./manage.py runserver

    Performing system checks...
    Django version 3.1.7, using settings 'cookbook.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CONTROL-C.

Go to localhost:8000/graphql and type your first query!

    query {
      allIngredients {
        edges {
          node {
            id,
            name
          }
        }
      }
    }

The above will return the names & IDs for all ingredients. But perhaps you want a specific ingredient:

    query {
      # Graphene creates globally unique IDs for all objects.
      # You may need to copy this value from the results of the first query
      ingredient(id: "SW5ncmVkaWVudE5vZGU6MQ==") {
        name
      }
    }

You can also get each ingredient for each category:

    query {
      allCategories {
        edges {
          node {
            name,
            ingredients {
              edges {
                node {
                  name
                }
              }
            }
          }
        }
      }
    }

Or you can get only ‘meat’ ingredients containing the letter ‘e’:

    query {
      # You can also use `category: "CATEGORY GLOBAL ID"`
      allIngredients(name_Icontains: "e", category_Name: "Meat") {
        edges {
          node {
            name
          }
        }
      }
    }

### Final Steps

We have created a GraphQL endpoint that will work with Relay, but for Relay to work it needs access to a (non python) schema. Instructions to export the schema can be found on the Introspection Schema part of this guide.