2019-04-26 18:48:37 +03:00
Basic Tutorial
2017-02-12 17:44:39 +03:00
===========================================
2019-04-26 18:48:37 +03:00
Graphene Django has a number of additional features that are designed to make
working with Django easy. Our primary focus in this tutorial is to give a good
2020-08-05 22:17:53 +03:00
understanding of how to connect models from Django ORM to Graphene object types.
2017-02-12 17:44:39 +03:00
2017-10-05 06:13:00 +03:00
Set up the Django project
-------------------------
2017-02-12 17:44:39 +03:00
2017-10-05 06:13:00 +03:00
We will set up the project, create the following:
2017-02-12 17:44:39 +03:00
- A Django project called `` cookbook ``
- An app within `` cookbook `` called `` ingredients ``
.. code :: bash
# 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
2020-08-05 22:17:53 +03:00
pip install django graphene_django
2017-02-12 17:44:39 +03:00
# Set up a new project with a single application
2020-08-05 22:17:53 +03:00
django-admin startproject cookbook . # Note the trailing '.' character
2017-02-12 17:44:39 +03:00
cd cookbook
2020-08-05 22:17:53 +03:00
django-admin startapp ingredients
2017-02-12 17:44:39 +03:00
Now sync your database for the first time:
.. code :: bash
2022-09-24 16:02:33 +03:00
cd ..
2017-02-12 17:44:39 +03:00
python manage.py migrate
Let's create a few simple models...
Defining our models
^^^^^^^^^^^^^^^^^^^
Let's get started with these models:
.. code :: python
# 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()
2017-12-02 21:35:17 +03:00
category = models.ForeignKey(
2020-08-05 22:17:53 +03:00
Category, related_name="ingredients", on_delete=models.CASCADE
)
2017-02-12 17:44:39 +03:00
def __str__(self):
return self.name
2017-07-18 00:51:21 +03:00
Add ingredients as INSTALLED_APPS:
.. code :: python
2020-08-05 22:17:53 +03:00
# cookbook/settings.py
2017-07-18 00:51:21 +03:00
INSTALLED_APPS = [
...
# Install the ingredients app
2020-08-05 22:17:53 +03:00
"cookbook.ingredients",
2017-07-18 00:51:21 +03:00
]
2022-09-24 16:02:33 +03:00
Make sure the app name in `` cookbook.ingredients.apps.IngredientsConfig `` is set to `` cookbook.ingredients `` .
.. code :: python
# cookbook/ingredients/apps.py
from django.apps import AppConfig
class IngredientsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'cookbook.ingredients'
2017-12-02 21:35:17 +03:00
2017-02-12 17:44:39 +03:00
Don't forget to create & run migrations:
.. code :: bash
python manage.py makemigrations
python manage.py migrate
2019-04-26 18:48:37 +03:00
2017-02-12 17:44:39 +03:00
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 <https://raw.githubusercontent.com/graphql-python/graphene-django/master/examples/cookbook/cookbook/ingredients/fixtures/ingredients.json>`__
fixture and place it in
`` cookbook/ingredients/fixtures/ingredients.json `` . You can then run the
following:
.. code :: bash
2020-08-05 22:17:53 +03:00
python manage.py loaddata ingredients
2017-02-12 17:44:39 +03:00
Installed 6 object(s) from 1 fixture(s)
2019-04-26 18:48:37 +03:00
2017-02-12 17:44:39 +03:00
Alternatively you can use the Django admin interface to create some data
yourself. You'll need to run the development server (see below), and
2020-08-05 22:17:53 +03:00
create a login for yourself too (`` python manage.py createsuperuser `` ).
2017-02-12 17:44:39 +03:00
2018-06-06 00:48:06 +03:00
Register models with admin panel:
.. code :: python
# cookbook/ingredients/admin.py
from django.contrib import admin
from cookbook.ingredients.models import Category, Ingredient
admin.site.register(Category)
admin.site.register(Ingredient)
2017-02-12 17:44:39 +03:00
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 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.
2020-08-05 22:17:53 +03:00
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.
2017-02-12 17:44:39 +03:00
After we've done that, we will list those types as fields in the `` Query `` class.
2020-08-05 22:17:53 +03:00
Create `` cookbook/schema.py `` and type the following:
2017-02-12 17:44:39 +03:00
.. code :: python
2020-08-05 22:17:53 +03:00
# cookbook/schema.py
2017-02-12 17:44:39 +03:00
import graphene
2020-08-05 22:17:53 +03:00
from graphene_django import DjangoObjectType
2017-02-12 17:44:39 +03:00
from cookbook.ingredients.models import Category, Ingredient
class CategoryType(DjangoObjectType):
class Meta:
model = Category
2020-08-05 22:17:53 +03:00
fields = ("id", "name", "ingredients")
2017-02-12 17:44:39 +03:00
class IngredientType(DjangoObjectType):
class Meta:
model = Ingredient
2020-08-05 22:17:53 +03:00
fields = ("id", "name", "notes", "category")
2017-02-12 17:44:39 +03:00
2020-08-05 22:17:53 +03:00
class Query(graphene.ObjectType):
2017-02-12 17:44:39 +03:00
all_ingredients = graphene.List(IngredientType)
2020-08-05 22:17:53 +03:00
category_by_name = graphene.Field(CategoryType, name=graphene.String(required=True))
2017-02-12 17:44:39 +03:00
2020-08-05 22:17:53 +03:00
def resolve_all_ingredients(root, info):
2017-02-12 17:44:39 +03:00
# We can easily optimize query count in the resolve method
2020-08-05 22:17:53 +03:00
return Ingredient.objects.select_related("category").all()
2017-02-12 17:44:39 +03:00
2020-08-05 22:17:53 +03:00
def resolve_category_by_name(root, info, name):
try:
return Category.objects.get(name=name)
except Category.DoesNotExist:
return None
2017-02-12 17:44:39 +03:00
schema = graphene.Schema(query=Query)
You can think of this as being something like your top-level `` urls.py ``
2020-08-05 22:17:53 +03:00
file.
2017-02-12 17:44:39 +03:00
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 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.
2017-07-18 00:51:21 +03:00
Add `` graphene_django `` to `` INSTALLED_APPS `` in `` cookbook/settings.py `` :
2017-02-12 17:44:39 +03:00
.. code :: python
2020-08-05 22:17:53 +03:00
# cookbook/settings.py
2017-02-12 17:44:39 +03:00
INSTALLED_APPS = [
...
2020-08-05 22:17:53 +03:00
"graphene_django",
2017-02-12 17:44:39 +03:00
]
And then add the `` SCHEMA `` to the `` GRAPHENE `` config in `` cookbook/settings.py `` :
.. code :: python
2020-08-05 22:17:53 +03:00
# cookbook/settings.py
2017-02-12 17:44:39 +03:00
GRAPHENE = {
2020-08-05 22:17:53 +03:00
"SCHEMA": "cookbook.schema.schema"
2017-02-12 17:44:39 +03:00
}
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
2017-02-12 17:48:48 +03:00
aforementioned GraphiQL we specify that on the parameters with `` graphiql=True `` .
2017-02-12 17:44:39 +03:00
.. code :: python
2020-08-05 22:17:53 +03:00
# cookbook/urls.py
2017-02-12 17:44:39 +03:00
from django.contrib import admin
2020-08-05 22:17:53 +03:00
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
2017-02-12 17:44:39 +03:00
from graphene_django.views import GraphQLView
urlpatterns = [
2020-08-05 22:17:53 +03:00
path("admin/", admin.site.urls),
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
2017-02-12 17:44:39 +03:00
]
If we didn't specify the target schema in the Django settings file
as explained above, we can do so here using:
.. code :: python
2020-08-05 22:17:53 +03:00
# cookbook/urls.py
2017-02-12 17:44:39 +03:00
from django.contrib import admin
2020-08-05 22:17:53 +03:00
from django.urls import path
from django.views.decorators.csrf import csrf_exempt
2017-02-12 17:44:39 +03:00
from graphene_django.views import GraphQLView
from cookbook.schema import schema
urlpatterns = [
2020-08-05 22:17:53 +03:00
path("admin/", admin.site.urls),
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))),
2017-02-12 17:44:39 +03:00
]
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.
.. code :: bash
2020-08-05 22:17:53 +03:00
python manage.py runserver
2017-02-12 17:44:39 +03:00
Performing system checks...
2020-08-05 22:17:53 +03:00
Django version 3.0.7, using settings 'cookbook.settings'
2017-02-12 17:44:39 +03:00
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
2017-02-21 22:39:25 +03:00
Go to `localhost:8000/graphql <http://localhost:8000/graphql> `__ and
2017-02-12 17:44:39 +03:00
type your first query!
.. code ::
query {
allIngredients {
id
name
}
}
If you are using the provided fixtures, you will see the following response:
.. code ::
{
"data": {
"allIngredients": [
{
"id": "1",
"name": "Eggs"
},
{
"id": "2",
"name": "Milk"
},
{
"id": "3",
"name": "Beef"
},
{
"id": "4",
"name": "Chicken"
}
]
}
}
2020-08-05 22:17:53 +03:00
Congratulations, you have created a working GraphQL server 🥳!
Note: Graphene `automatically camelcases <http://docs.graphene-python.org/en/latest/types/schema/#auto-camelcase-field-names> `__ all field names for better compatibility with JavaScript clients.
2017-02-12 17:44:39 +03:00
Getting relations
-----------------
2020-08-05 22:17:53 +03:00
Using the current schema we can query for relations too. This is where GraphQL becomes really powerful!
2017-02-12 17:44:39 +03:00
2020-08-05 22:17:53 +03:00
For example, we may want to get a specific categories and list all ingredients that are in that category.
2017-02-12 17:44:39 +03:00
We can do that with the following query:
.. code ::
query {
2020-08-05 22:17:53 +03:00
categoryByName(name: "Dairy") {
2017-02-12 17:44:39 +03:00
id
name
ingredients {
id
name
}
}
}
This will give you (in case you are using the fixtures) the following result:
.. code ::
{
"data": {
2020-08-05 22:17:53 +03:00
"categoryByName": {
"id": "1",
"name": "Dairy",
"ingredients": [
{
"id": "1",
"name": "Eggs"
},
{
"id": "2",
"name": "Milk"
}
]
}
2017-02-12 17:44:39 +03:00
}
}
We can also list all ingredients and get information for the category they are in:
.. code ::
query {
allIngredients {
id
name
category {
id
name
}
}
}
2017-02-14 20:55:38 +03:00
Summary
-------
2020-08-05 22:17:53 +03:00
As you can see, GraphQL is very powerful and integrating Django models allows you to get started with a working server quickly.
2017-02-14 20:55:38 +03:00
2020-08-05 22:17:53 +03:00
If you want to put things like `` django-filter `` and automatic pagination in action, you should continue with the :ref: `Relay tutorial` .
2019-04-26 18:48:37 +03:00
2020-08-05 22:17:53 +03:00
A good idea is to check the `Graphene <http://docs.graphene-python.org/en/latest/> `__
documentation so that you are familiar with it as well.