mirror of
https://github.com/graphql-python/graphene-django.git
synced 2024-11-22 09:37:07 +03:00
Update tutorial docs (#994)
This commit is contained in:
parent
b552dcac24
commit
97de26bf2e
|
@ -25,8 +25,8 @@ Add ``graphene_django`` to the ``INSTALLED_APPS`` in the ``settings.py`` file of
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
...
|
...
|
||||||
'django.contrib.staticfiles', # Required for GraphiQL
|
"django.contrib.staticfiles", # Required for GraphiQL
|
||||||
'graphene_django'
|
"graphene_django"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ Finally, define the schema location for Graphene in the ``settings.py`` file of
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
GRAPHENE = {
|
GRAPHENE = {
|
||||||
'SCHEMA': 'django_root.schema.schema'
|
"SCHEMA": "django_root.schema.schema"
|
||||||
}
|
}
|
||||||
|
|
||||||
Where ``path.schema.schema`` is the location of the ``Schema`` object in your Django project.
|
Where ``path.schema.schema`` is the location of the ``Schema`` object in your Django project.
|
||||||
|
@ -75,7 +75,7 @@ The most basic ``schema.py`` looks like this:
|
||||||
import graphene
|
import graphene
|
||||||
|
|
||||||
class Query(graphene.ObjectType):
|
class Query(graphene.ObjectType):
|
||||||
pass
|
hello = graphene.String(default_value="Hi!")
|
||||||
|
|
||||||
schema = graphene.Schema(query=Query)
|
schema = graphene.Schema(query=Query)
|
||||||
|
|
||||||
|
|
121
docs/queries.rst
121
docs/queries.rst
|
@ -20,27 +20,26 @@ Full example
|
||||||
# my_app/schema.py
|
# my_app/schema.py
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
|
||||||
from graphene_django.types import DjangoObjectType
|
|
||||||
from .models import Question
|
from .models import Question
|
||||||
|
|
||||||
|
|
||||||
class QuestionType(DjangoObjectType):
|
class QuestionType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
|
fields = ("id", "question_text")
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
class Query:
|
|
||||||
questions = graphene.List(QuestionType)
|
questions = graphene.List(QuestionType)
|
||||||
question = graphene.Field(QuestionType, question_id=graphene.String())
|
question_by_id = graphene.Field(QuestionType, id=graphene.String())
|
||||||
|
|
||||||
def resolve_questions(self, info, **kwargs):
|
def resolve_questions(root, info, **kwargs):
|
||||||
# Querying a list
|
# Querying a list
|
||||||
return Question.objects.all()
|
return Question.objects.all()
|
||||||
|
|
||||||
def resolve_question(self, info, question_id):
|
def resolve_question_by_id(root, info, id):
|
||||||
# Querying a single question
|
# Querying a single question
|
||||||
return Question.objects.get(pk=question_id)
|
return Question.objects.get(pk=id)
|
||||||
|
|
||||||
|
|
||||||
Specifying which fields to include
|
Specifying which fields to include
|
||||||
|
@ -60,21 +59,27 @@ Show **only** these fields on the model:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from .models import Question
|
||||||
|
|
||||||
class QuestionType(DjangoObjectType):
|
class QuestionType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
fields = ('id', 'question_text')
|
fields = ("id", "question_text")
|
||||||
|
|
||||||
You can also set the ``fields`` attribute to the special value ``'__all__'`` to indicate that all fields in the model should be used.
|
You can also set the ``fields`` attribute to the special value ``"__all__"`` to indicate that all fields in the model should be used.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from .models import Question
|
||||||
|
|
||||||
class QuestionType(DjangoObjectType):
|
class QuestionType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
fields = '__all__'
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
``exclude``
|
``exclude``
|
||||||
|
@ -84,10 +89,13 @@ Show all fields **except** those in ``exclude``:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from .models import Question
|
||||||
|
|
||||||
class QuestionType(DjangoObjectType):
|
class QuestionType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
exclude = ('question_text',)
|
exclude = ("question_text",)
|
||||||
|
|
||||||
|
|
||||||
Customising fields
|
Customising fields
|
||||||
|
@ -97,16 +105,19 @@ You can completely overwrite a field, or add new fields, to a ``DjangoObjectType
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from .models import Question
|
||||||
|
|
||||||
class QuestionType(DjangoObjectType):
|
class QuestionType(DjangoObjectType):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
fields = ('id', 'question_text')
|
fields = ("id", "question_text")
|
||||||
|
|
||||||
extra_field = graphene.String()
|
extra_field = graphene.String()
|
||||||
|
|
||||||
def resolve_extra_field(self, info):
|
def resolve_extra_field(self, info):
|
||||||
return 'hello!'
|
return "hello!"
|
||||||
|
|
||||||
|
|
||||||
Choices to Enum conversion
|
Choices to Enum conversion
|
||||||
|
@ -121,12 +132,19 @@ For example the following ``Model`` and ``DjangoObjectType``:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
|
||||||
class PetModel(models.Model):
|
class PetModel(models.Model):
|
||||||
kind = models.CharField(max_length=100, choices=(('cat', 'Cat'), ('dog', 'Dog')))
|
kind = models.CharField(
|
||||||
|
max_length=100,
|
||||||
|
choices=(("cat", "Cat"), ("dog", "Dog"))
|
||||||
|
)
|
||||||
|
|
||||||
class Pet(DjangoObjectType):
|
class Pet(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PetModel
|
model = PetModel
|
||||||
|
fields = ("id", "kind",)
|
||||||
|
|
||||||
Results in the following GraphQL schema definition:
|
Results in the following GraphQL schema definition:
|
||||||
|
|
||||||
|
@ -148,9 +166,13 @@ You can disable this automatic conversion by setting
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from .models import PetModel
|
||||||
|
|
||||||
class Pet(DjangoObjectType):
|
class Pet(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PetModel
|
model = PetModel
|
||||||
|
fields = ("id", "kind",)
|
||||||
convert_choices_to_enum = False
|
convert_choices_to_enum = False
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
@ -165,10 +187,14 @@ automatically converted into enums:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from .models import PetModel
|
||||||
|
|
||||||
class Pet(DjangoObjectType):
|
class Pet(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PetModel
|
model = PetModel
|
||||||
convert_choices_to_enum = ['kind']
|
fields = ("id", "kind",)
|
||||||
|
convert_choices_to_enum = ["kind"]
|
||||||
|
|
||||||
**Note:** Setting ``convert_choices_to_enum = []`` is the same as setting it to
|
**Note:** Setting ``convert_choices_to_enum = []`` is the same as setting it to
|
||||||
``False``.
|
``False``.
|
||||||
|
@ -181,6 +207,8 @@ Say you have the following models:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
foo = models.CharField(max_length=256)
|
foo = models.CharField(max_length=256)
|
||||||
|
|
||||||
|
@ -192,10 +220,13 @@ When ``Question`` is published as a ``DjangoObjectType`` and you want to add ``C
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from .models import Question
|
||||||
|
|
||||||
class QuestionType(DjangoObjectType):
|
class QuestionType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
fields = ('category',)
|
fields = ("category",)
|
||||||
|
|
||||||
Then all query-able related models must be defined as DjangoObjectType subclass,
|
Then all query-able related models must be defined as DjangoObjectType subclass,
|
||||||
or they will fail to show if you are trying to query those relation fields. You only
|
or they will fail to show if you are trying to query those relation fields. You only
|
||||||
|
@ -203,9 +234,13 @@ need to create the most basic class for this to work:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
|
from .models import Category
|
||||||
|
|
||||||
class CategoryType(DjangoObjectType):
|
class CategoryType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Category
|
model = Category
|
||||||
|
fields = ("foo",)
|
||||||
|
|
||||||
.. _django-objecttype-get-queryset:
|
.. _django-objecttype-get-queryset:
|
||||||
|
|
||||||
|
@ -220,7 +255,6 @@ Use this to control filtering on the ObjectType level instead of the Query objec
|
||||||
from graphene_django.types import DjangoObjectType
|
from graphene_django.types import DjangoObjectType
|
||||||
from .models import Question
|
from .models import Question
|
||||||
|
|
||||||
|
|
||||||
class QuestionType(DjangoObjectType):
|
class QuestionType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
|
@ -240,18 +274,22 @@ This resolve method should follow this format:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
def resolve_foo(self, info, **kwargs):
|
def resolve_foo(parent, info, **kwargs):
|
||||||
|
|
||||||
Where "foo" is the name of the field declared in the ``Query`` object.
|
Where "foo" is the name of the field declared in the ``Query`` object.
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
class Query:
|
import graphene
|
||||||
|
from .models import Question
|
||||||
|
from .types import QuestionType
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
foo = graphene.List(QuestionType)
|
foo = graphene.List(QuestionType)
|
||||||
|
|
||||||
def resolve_foo(self, info, **kwargs):
|
def resolve_foo(root, info):
|
||||||
id = kwargs.get('id')
|
id = kwargs.get("id")
|
||||||
return QuestionModel.objects.get(id)
|
return Question.objects.get(id)
|
||||||
|
|
||||||
Arguments
|
Arguments
|
||||||
~~~~~~~~~
|
~~~~~~~~~
|
||||||
|
@ -260,10 +298,18 @@ Additionally, Resolvers will receive **any arguments declared in the field defin
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
class Query:
|
import graphene
|
||||||
question = graphene.Field(Question, foo=graphene.String(), bar=graphene.Int())
|
from .models import Question
|
||||||
|
from .types import QuestionType
|
||||||
|
|
||||||
def resolve_question(self, info, foo, bar):
|
class Query(graphene.ObjectType):
|
||||||
|
question = graphene.Field(
|
||||||
|
QuestionType,
|
||||||
|
foo=graphene.String(),
|
||||||
|
bar=graphene.Int()
|
||||||
|
)
|
||||||
|
|
||||||
|
def resolve_question(root, info, foo, bar):
|
||||||
# If `foo` or `bar` are declared in the GraphQL query they will be here, else None.
|
# If `foo` or `bar` are declared in the GraphQL query they will be here, else None.
|
||||||
return Question.objects.filter(foo=foo, bar=bar).first()
|
return Question.objects.filter(foo=foo, bar=bar).first()
|
||||||
|
|
||||||
|
@ -278,7 +324,15 @@ of Django's ``HTTPRequest`` in your resolve methods, such as checking for authen
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
def resolve_questions(self, info, **kwargs):
|
import graphene
|
||||||
|
|
||||||
|
from .models import Question
|
||||||
|
from .types import QuestionType
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
|
questions = graphene.List(QuestionType)
|
||||||
|
|
||||||
|
def resolve_questions(root, info):
|
||||||
# See if a user is authenticated
|
# See if a user is authenticated
|
||||||
if info.context.user.is_authenticated():
|
if info.context.user.is_authenticated():
|
||||||
return Question.objects.all()
|
return Question.objects.all()
|
||||||
|
@ -305,15 +359,13 @@ Django models and your external API.
|
||||||
import graphene
|
import graphene
|
||||||
from .models import Question
|
from .models import Question
|
||||||
|
|
||||||
|
|
||||||
class MyQuestion(graphene.ObjectType):
|
class MyQuestion(graphene.ObjectType):
|
||||||
text = graphene.String()
|
text = graphene.String()
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
class Query:
|
|
||||||
question = graphene.Field(MyQuestion, question_id=graphene.String())
|
question = graphene.Field(MyQuestion, question_id=graphene.String())
|
||||||
|
|
||||||
def resolve_question(self, info, question_id):
|
def resolve_question(root, info, question_id):
|
||||||
question = Question.objects.get(pk=question_id)
|
question = Question.objects.get(pk=question_id)
|
||||||
return MyQuestion(
|
return MyQuestion(
|
||||||
text=question.question_text
|
text=question.question_text
|
||||||
|
@ -343,25 +395,22 @@ the core graphene pages for more information on customizing the Relay experience
|
||||||
from graphene_django import DjangoObjectType
|
from graphene_django import DjangoObjectType
|
||||||
from .models import Question
|
from .models import Question
|
||||||
|
|
||||||
|
|
||||||
class QuestionType(DjangoObjectType):
|
class QuestionType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Question
|
model = Question
|
||||||
interfaces = (relay.Node,)
|
interfaces = (relay.Node,) # make sure you add this
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
class QuestionConnection(relay.Connection):
|
class QuestionConnection(relay.Connection):
|
||||||
class Meta:
|
class Meta:
|
||||||
node = QuestionType
|
node = QuestionType
|
||||||
|
|
||||||
|
|
||||||
class Query:
|
class Query:
|
||||||
questions = relay.ConnectionField(QuestionConnection)
|
questions = relay.ConnectionField(QuestionConnection)
|
||||||
|
|
||||||
def resolve_questions(root, info, **kwargs):
|
def resolve_questions(root, info, **kwargs):
|
||||||
return Question.objects.all()
|
return Question.objects.all()
|
||||||
|
|
||||||
|
|
||||||
You can now execute queries like:
|
You can now execute queries like:
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,11 @@ Basic Tutorial
|
||||||
|
|
||||||
Graphene Django has a number of additional features that are designed to make
|
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
|
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.
|
understanding of how to connect models from Django ORM to Graphene object types.
|
||||||
|
|
||||||
Set up the Django project
|
Set up the Django project
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
You can find the entire project in ``examples/cookbook-plain``.
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
We will set up the project, create the following:
|
We will set up the project, create the following:
|
||||||
|
|
||||||
- A Django project called ``cookbook``
|
- A Django project called ``cookbook``
|
||||||
|
@ -28,13 +24,12 @@ We will set up the project, create the following:
|
||||||
source env/bin/activate # On Windows use `env\Scripts\activate`
|
source env/bin/activate # On Windows use `env\Scripts\activate`
|
||||||
|
|
||||||
# Install Django and Graphene with Django support
|
# Install Django and Graphene with Django support
|
||||||
pip install django
|
pip install django graphene_django
|
||||||
pip install graphene_django
|
|
||||||
|
|
||||||
# Set up a new project with a single application
|
# Set up a new project with a single application
|
||||||
django-admin.py startproject cookbook . # Note the trailing '.' character
|
django-admin startproject cookbook . # Note the trailing '.' character
|
||||||
cd cookbook
|
cd cookbook
|
||||||
django-admin.py startapp ingredients
|
django-admin startapp ingredients
|
||||||
|
|
||||||
Now sync your database for the first time:
|
Now sync your database for the first time:
|
||||||
|
|
||||||
|
@ -54,19 +49,18 @@ Let's get started with these models:
|
||||||
# cookbook/ingredients/models.py
|
# cookbook/ingredients/models.py
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class Ingredient(models.Model):
|
class Ingredient(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
notes = models.TextField()
|
notes = models.TextField()
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
Category, related_name='ingredients', on_delete=models.CASCADE)
|
Category, related_name="ingredients", on_delete=models.CASCADE
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -75,10 +69,12 @@ Add ingredients as INSTALLED_APPS:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
# cookbook/settings.py
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
...
|
...
|
||||||
# Install the ingredients app
|
# Install the ingredients app
|
||||||
'cookbook.ingredients',
|
"cookbook.ingredients",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,13 +98,13 @@ following:
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
$ python ./manage.py loaddata ingredients
|
python manage.py loaddata ingredients
|
||||||
|
|
||||||
Installed 6 object(s) from 1 fixture(s)
|
Installed 6 object(s) from 1 fixture(s)
|
||||||
|
|
||||||
Alternatively you can use the Django admin interface to create some data
|
Alternatively you can use the Django admin interface to create some data
|
||||||
yourself. You'll need to run the development server (see below), and
|
yourself. You'll need to run the development server (see below), and
|
||||||
create a login for yourself too (``./manage.py createsuperuser``).
|
create a login for yourself too (``python manage.py createsuperuser``).
|
||||||
|
|
||||||
Register models with admin panel:
|
Register models with admin panel:
|
||||||
|
|
||||||
|
@ -138,66 +134,48 @@ order to create this representation, Graphene needs to know about each
|
||||||
This graph also has a *root type* through which all access begins. This
|
This graph also has a *root type* through which all access begins. This
|
||||||
is the ``Query`` class below.
|
is the ``Query`` class below.
|
||||||
|
|
||||||
This means, for each of our models, we are going to create a type, subclassing ``DjangoObjectType``
|
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.
|
After we've done that, we will list those types as fields in the ``Query`` class.
|
||||||
|
|
||||||
Create ``cookbook/ingredients/schema.py`` and type the following:
|
Create ``cookbook/schema.py`` and type the following:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
# cookbook/ingredients/schema.py
|
# cookbook/schema.py
|
||||||
import graphene
|
import graphene
|
||||||
|
from graphene_django import DjangoObjectType
|
||||||
from graphene_django.types import DjangoObjectType
|
|
||||||
|
|
||||||
from cookbook.ingredients.models import Category, Ingredient
|
from cookbook.ingredients.models import Category, Ingredient
|
||||||
|
|
||||||
|
|
||||||
class CategoryType(DjangoObjectType):
|
class CategoryType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Category
|
model = Category
|
||||||
|
fields = ("id", "name", "ingredients")
|
||||||
|
|
||||||
class IngredientType(DjangoObjectType):
|
class IngredientType(DjangoObjectType):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Ingredient
|
model = Ingredient
|
||||||
|
fields = ("id", "name", "notes", "category")
|
||||||
|
|
||||||
|
class Query(graphene.ObjectType):
|
||||||
class Query(object):
|
|
||||||
all_categories = graphene.List(CategoryType)
|
|
||||||
all_ingredients = graphene.List(IngredientType)
|
all_ingredients = graphene.List(IngredientType)
|
||||||
|
category_by_name = graphene.Field(CategoryType, name=graphene.String(required=True))
|
||||||
|
|
||||||
def resolve_all_categories(self, info, **kwargs):
|
def resolve_all_ingredients(root, info):
|
||||||
return Category.objects.all()
|
|
||||||
|
|
||||||
def resolve_all_ingredients(self, info, **kwargs):
|
|
||||||
# We can easily optimize query count in the resolve method
|
# We can easily optimize query count in the resolve method
|
||||||
return Ingredient.objects.select_related('category').all()
|
return Ingredient.objects.select_related("category").all()
|
||||||
|
|
||||||
|
def resolve_category_by_name(root, info, name):
|
||||||
Note that the above ``Query`` class is a mixin, inheriting from
|
try:
|
||||||
``object``. This is because we will now create a project-level query
|
return Category.objects.get(name=name)
|
||||||
class which will combine all our app-level mixins.
|
except Category.DoesNotExist:
|
||||||
|
return None
|
||||||
Create the parent project-level ``cookbook/schema.py``:
|
|
||||||
|
|
||||||
.. code:: python
|
|
||||||
|
|
||||||
import graphene
|
|
||||||
|
|
||||||
import cookbook.ingredients.schema
|
|
||||||
|
|
||||||
|
|
||||||
class Query(cookbook.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)
|
schema = graphene.Schema(query=Query)
|
||||||
|
|
||||||
You can think of this as being something like your top-level ``urls.py``
|
You can think of this as being something like your top-level ``urls.py``
|
||||||
file (although it currently lacks any namespacing).
|
file.
|
||||||
|
|
||||||
Testing everything so far
|
Testing everything so far
|
||||||
-------------------------
|
-------------------------
|
||||||
|
@ -216,18 +194,21 @@ Add ``graphene_django`` to ``INSTALLED_APPS`` in ``cookbook/settings.py``:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
# cookbook/settings.py
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
...
|
...
|
||||||
# This will also make the `graphql_schema` management command available
|
"graphene_django",
|
||||||
'graphene_django',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
And then add the ``SCHEMA`` to the ``GRAPHENE`` config in ``cookbook/settings.py``:
|
And then add the ``SCHEMA`` to the ``GRAPHENE`` config in ``cookbook/settings.py``:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
|
# cookbook/settings.py
|
||||||
|
|
||||||
GRAPHENE = {
|
GRAPHENE = {
|
||||||
'SCHEMA': 'cookbook.schema.schema'
|
"SCHEMA": "cookbook.schema.schema"
|
||||||
}
|
}
|
||||||
|
|
||||||
Alternatively, we can specify the schema to be used in the urls definition,
|
Alternatively, we can specify the schema to be used in the urls definition,
|
||||||
|
@ -245,14 +226,17 @@ aforementioned GraphiQL we specify that on the parameters with ``graphiql=True``
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
# cookbook/urls.py
|
||||||
|
|
||||||
from django.contrib import admin
|
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 graphene_django.views import GraphQLView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^admin/', admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
url(r'^graphql$', GraphQLView.as_view(graphiql=True)),
|
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True))),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -261,16 +245,19 @@ as explained above, we can do so here using:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
# cookbook/urls.py
|
||||||
|
|
||||||
from django.contrib import admin
|
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 graphene_django.views import GraphQLView
|
||||||
|
|
||||||
from cookbook.schema import schema
|
from cookbook.schema import schema
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^admin/', admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
url(r'^graphql$', GraphQLView.as_view(graphiql=True, schema=schema)),
|
path("graphql", csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -283,10 +270,10 @@ from the command line.
|
||||||
|
|
||||||
.. code:: bash
|
.. code:: bash
|
||||||
|
|
||||||
$ python ./manage.py runserver
|
python manage.py runserver
|
||||||
|
|
||||||
Performing system checks...
|
Performing system checks...
|
||||||
Django version 1.11, using settings 'cookbook.settings'
|
Django version 3.0.7, using settings 'cookbook.settings'
|
||||||
Starting development server at http://127.0.0.1:8000/
|
Starting development server at http://127.0.0.1:8000/
|
||||||
Quit the server with CONTROL-C.
|
Quit the server with CONTROL-C.
|
||||||
|
|
||||||
|
@ -329,24 +316,25 @@ If you are using the provided fixtures, you will see the following response:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
You can experiment with ``allCategories`` too.
|
|
||||||
|
|
||||||
Something to have in mind is the `auto camelcasing <http://docs.graphene-python.org/en/latest/types/schema/#auto-camelcase-field-names>`__ that is happening.
|
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
|
Getting relations
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Right now, with this simple setup in place, we can query for relations too. This is where graphql becomes really powerful!
|
Using the current schema we can query for relations too. This is where GraphQL becomes really powerful!
|
||||||
|
|
||||||
For example, we may want to list all categories and in each category, all ingredients that are in that category.
|
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:
|
We can do that with the following query:
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
query {
|
query {
|
||||||
allCategories {
|
categoryByName(name: "Dairy") {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
ingredients {
|
ingredients {
|
||||||
|
@ -356,15 +344,13 @@ We can do that with the following query:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
This will give you (in case you are using the fixtures) the following result:
|
This will give you (in case you are using the fixtures) the following result:
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
|
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"allCategories": [
|
"categoryByName": {
|
||||||
{
|
|
||||||
"id": "1",
|
"id": "1",
|
||||||
"name": "Dairy",
|
"name": "Dairy",
|
||||||
"ingredients": [
|
"ingredients": [
|
||||||
|
@ -377,22 +363,7 @@ This will give you (in case you are using the fixtures) the following result:
|
||||||
"name": "Milk"
|
"name": "Milk"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2",
|
|
||||||
"name": "Meat",
|
|
||||||
"ingredients": [
|
|
||||||
{
|
|
||||||
"id": "3",
|
|
||||||
"name": "Beef"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "4",
|
|
||||||
"name": "Chicken"
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,71 +382,12 @@ We can also list all ingredients and get information for the category they are i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Getting single objects
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
So far, we have been able to fetch list of objects and follow relation. But what about single objects?
|
|
||||||
|
|
||||||
We can update our schema to support that, by adding new query for ``ingredient`` and ``category`` and adding arguments, so we can query for specific objects.
|
|
||||||
Add the **Highlighted** lines to ``cookbook/ingredients/schema.py``
|
|
||||||
|
|
||||||
.. literalinclude:: schema.py
|
|
||||||
:emphasize-lines: 19-21,25-27,36-58
|
|
||||||
|
|
||||||
Now, with the code in place, we can query for single objects.
|
|
||||||
|
|
||||||
For example, lets query ``category``:
|
|
||||||
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
query {
|
|
||||||
category(id: 1) {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
anotherCategory: category(name: "Dairy") {
|
|
||||||
ingredients {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
This will give us the following results:
|
|
||||||
|
|
||||||
.. code::
|
|
||||||
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"category": {
|
|
||||||
"name": "Dairy"
|
|
||||||
},
|
|
||||||
"anotherCategory": {
|
|
||||||
"ingredients": [
|
|
||||||
{
|
|
||||||
"id": "1",
|
|
||||||
"name": "Eggs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2",
|
|
||||||
"name": "Milk"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
As an exercise, you can try making some queries to ``ingredient``.
|
|
||||||
|
|
||||||
Something to keep in mind - since we are using one field several times in our query, we need `aliases <http://graphql.org/learn/queries/#aliases>`__
|
|
||||||
|
|
||||||
|
|
||||||
Summary
|
Summary
|
||||||
-------
|
-------
|
||||||
|
|
||||||
As you can see, GraphQL is very powerful but there are a lot of repetitions in our example. We can do a lot of improvements by adding layers of abstraction on top of ``graphene-django``.
|
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.**
|
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/>`__
|
A good idea is to check the `Graphene <http://docs.graphene-python.org/en/latest/>`__
|
||||||
documentation but it is not essential to understand and use Graphene-Django in your project.
|
documentation so that you are familiar with it as well.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
.. _Relay tutorial:
|
||||||
|
|
||||||
Relay tutorial
|
Relay tutorial
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user