mirror of
				https://github.com/graphql-python/graphene-django.git
				synced 2025-11-04 09:57:53 +03:00 
			
		
		
		
	* 🔧 Add pre-commit config Similar to graphene and graphene-sqlalchemy * ⬆ Bump black * 👷 Lint on CI * ⬆ Bump flake8-black * 🔧 Keep excluding migrations * ⬆ Bump flake8 * 🔧 Remove black and flake8 from tox config * ⬆ Update pre-commit versions * Upgrade syntax to python 3.7+ * Format with pre-commit dedent docs/schema.py to allow formatting * Fix tests on python 3.7
		
			
				
	
	
		
			407 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			407 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
Basic Tutorial
 | 
						|
===========================================
 | 
						|
 | 
						|
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
 | 
						|
understanding 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``
 | 
						|
 | 
						|
.. 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
 | 
						|
    pip install django graphene_django
 | 
						|
 | 
						|
    # Set up a new project with a single application
 | 
						|
    django-admin startproject cookbook .  # Note the trailing '.' character
 | 
						|
    cd cookbook
 | 
						|
    django-admin startapp ingredients
 | 
						|
 | 
						|
Now sync your database for the first time:
 | 
						|
 | 
						|
.. code:: bash
 | 
						|
 | 
						|
    cd ..
 | 
						|
    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()
 | 
						|
        category = models.ForeignKey(
 | 
						|
            Category, related_name="ingredients", on_delete=models.CASCADE
 | 
						|
        )
 | 
						|
 | 
						|
        def __str__(self):
 | 
						|
            return self.name
 | 
						|
 | 
						|
Add ingredients as INSTALLED_APPS:
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
    # cookbook/settings.py
 | 
						|
 | 
						|
    INSTALLED_APPS = [
 | 
						|
        ...
 | 
						|
        # Install the ingredients app
 | 
						|
        "cookbook.ingredients",
 | 
						|
    ]
 | 
						|
 | 
						|
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'
 | 
						|
 | 
						|
Don't forget to create & run migrations:
 | 
						|
 | 
						|
.. code:: bash
 | 
						|
 | 
						|
    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 <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
 | 
						|
 | 
						|
    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 (``python manage.py createsuperuser``).
 | 
						|
 | 
						|
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)
 | 
						|
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
    # cookbook/schema.py
 | 
						|
    import graphene
 | 
						|
    from graphene_django import DjangoObjectType
 | 
						|
 | 
						|
    from cookbook.ingredients.models import Category, Ingredient
 | 
						|
 | 
						|
    class CategoryType(DjangoObjectType):
 | 
						|
        class Meta:
 | 
						|
            model = Category
 | 
						|
            fields = ("id", "name", "ingredients")
 | 
						|
 | 
						|
    class IngredientType(DjangoObjectType):
 | 
						|
        class Meta:
 | 
						|
            model = Ingredient
 | 
						|
            fields = ("id", "name", "notes", "category")
 | 
						|
 | 
						|
    class Query(graphene.ObjectType):
 | 
						|
        all_ingredients = graphene.List(IngredientType)
 | 
						|
        category_by_name = graphene.Field(CategoryType, name=graphene.String(required=True))
 | 
						|
 | 
						|
        def resolve_all_ingredients(root, info):
 | 
						|
            # We can easily optimize query count in the resolve method
 | 
						|
            return Ingredient.objects.select_related("category").all()
 | 
						|
 | 
						|
        def resolve_category_by_name(root, info, name):
 | 
						|
            try:
 | 
						|
                return Category.objects.get(name=name)
 | 
						|
            except Category.DoesNotExist:
 | 
						|
                return None
 | 
						|
 | 
						|
    schema = graphene.Schema(query=Query)
 | 
						|
 | 
						|
You can think of this as being something like your top-level ``urls.py``
 | 
						|
file.
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
Add ``graphene_django`` to ``INSTALLED_APPS`` in ``cookbook/settings.py``:
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
    # cookbook/settings.py
 | 
						|
 | 
						|
    INSTALLED_APPS = [
 | 
						|
        ...
 | 
						|
        "graphene_django",
 | 
						|
    ]
 | 
						|
 | 
						|
And then add the ``SCHEMA`` to the ``GRAPHENE`` config in ``cookbook/settings.py``:
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
    # 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 parameters with ``graphiql=True``.
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
    # cookbook/urls.py
 | 
						|
 | 
						|
    from django.contrib import admin
 | 
						|
    from django.urls import path
 | 
						|
    from django.views.decorators.csrf import csrf_exempt
 | 
						|
 | 
						|
    from graphene_django.views import GraphQLView
 | 
						|
 | 
						|
    urlpatterns = [
 | 
						|
        path("admin/", admin.site.urls),
 | 
						|
        path("graphql", csrf_exempt(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:
 | 
						|
 | 
						|
.. code:: python
 | 
						|
 | 
						|
    # cookbook/urls.py
 | 
						|
 | 
						|
    from django.contrib import admin
 | 
						|
    from django.urls import path
 | 
						|
    from django.views.decorators.csrf import csrf_exempt
 | 
						|
 | 
						|
    from graphene_django.views import GraphQLView
 | 
						|
 | 
						|
    from cookbook.schema import schema
 | 
						|
 | 
						|
    urlpatterns = [
 | 
						|
        path("admin/", admin.site.urls),
 | 
						|
        path("graphql", csrf_exempt(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.
 | 
						|
 | 
						|
.. code:: bash
 | 
						|
 | 
						|
    python manage.py runserver
 | 
						|
 | 
						|
    Performing system checks...
 | 
						|
    Django version 3.0.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 <http://localhost:8000/graphql>`__ and
 | 
						|
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"
 | 
						|
          }
 | 
						|
        ]
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
 | 
						|
Getting relations
 | 
						|
-----------------
 | 
						|
 | 
						|
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:
 | 
						|
 | 
						|
.. code::
 | 
						|
 | 
						|
    query {
 | 
						|
      categoryByName(name: "Dairy") {
 | 
						|
        id
 | 
						|
        name
 | 
						|
        ingredients {
 | 
						|
          id
 | 
						|
          name
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
This will give you (in case you are using the fixtures) the following result:
 | 
						|
 | 
						|
.. code::
 | 
						|
 | 
						|
    {
 | 
						|
      "data": {
 | 
						|
        "categoryByName": {
 | 
						|
          "id": "1",
 | 
						|
          "name": "Dairy",
 | 
						|
          "ingredients": [
 | 
						|
            {
 | 
						|
              "id": "1",
 | 
						|
              "name": "Eggs"
 | 
						|
            },
 | 
						|
            {
 | 
						|
              "id": "2",
 | 
						|
              "name": "Milk"
 | 
						|
            }
 | 
						|
          ]
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
We can also list all ingredients and get information for the category they are in:
 | 
						|
 | 
						|
.. code::
 | 
						|
 | 
						|
    query {
 | 
						|
      allIngredients {
 | 
						|
        id
 | 
						|
        name
 | 
						|
        category {
 | 
						|
          id
 | 
						|
          name
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
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 :ref:`Relay tutorial`.
 | 
						|
 | 
						|
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.
 |